{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JHSdxPJuN4x7",
    "outputId": "a74dc274-dd24-4415-a3e4-1fcff4e437a2",
    "ExecuteTime": {
     "end_time": "2025-02-05T01:49:06.736151Z",
     "start_time": "2025-02-05T01:48:58.651110Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "\n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n",
    "torch.manual_seed(seed)\n",
    "torch.cuda.manual_seed_all(seed)\n",
    "np.random.seed(seed)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "source": [
    "#挂载谷歌云盘\n",
    "\n",
    "# from google.colab import drive\n",
    "# drive.mount('/content/drive')"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "yQQEArYqWylq",
    "outputId": "bc80eeee-442a-4ff2-f69e-348314f84bce",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.541540Z",
     "start_time": "2024-08-05T08:06:22.530536400Z"
    }
   },
   "execution_count": 3,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# !cp /content/drive/MyDrive/transformer-de-en/* . -r"
   ],
   "metadata": {
    "id": "wzS4AimwWz7f",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.552601Z",
     "start_time": "2024-08-05T08:06:22.536531800Z"
    }
   },
   "execution_count": 4,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "55aNBForN4x9"
   },
   "source": [
    "## 数据加载\n",
    "\n",
    "- 采用WMT16的德语和英语平行语料库，数据集主页：[WMT16](https://www.statmt.org/wmt16/multimodal-task.html#task1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "SkjBEbr1N4x-",
    "outputId": "4b9817d0-616b-41a0-ceb7-9ff6bf0f42a1",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.608302700Z",
     "start_time": "2024-08-05T08:06:22.548590200Z"
    }
   },
   "outputs": [],
   "source": [
    "#和jieba分词类似\n",
    "# !pip install sacremoses\n",
    "# !pip install subword-nmt\n",
    "# # BPE分词\n"
   ]
  },
  {
   "cell_type": "code",
   "source": "!sh data_multi30k.sh wmt16 wmt16_cut de en",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Tc8aCQX-XrGv",
    "outputId": "67964082-2737-4d47-dd66-455e4ae69660",
    "ExecuteTime": {
     "end_time": "2025-02-05T01:49:49.647989Z",
     "start_time": "2025-02-05T01:49:33.853411Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[train] 源语言文本分词完成\n",
      "[train] 目标语言文本分词完成\n",
      "[val] 源语言文本分词完成\n",
      "[val] 目标语言文本分词完成\n",
      "[test] 源语言文本分词完成\n",
      "[test] 目标语言文本分词完成\n",
      "Finished applying bpe to train files.\n",
      "Finished applying bpe to val files.\n",
      "Finished applying bpe to test files.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "  0%|          | 0/20000 [00:00<?, ?it/s]\n",
      "  0%|          | 19/20000 [00:00<01:45, 189.35it/s]\n",
      "  0%|          | 59/20000 [00:00<01:03, 312.91it/s]\n",
      "  1%|          | 155/20000 [00:00<00:32, 603.02it/s]\n",
      "  1%|1         | 288/20000 [00:00<00:22, 885.19it/s]\n",
      "  2%|2         | 431/20000 [00:00<00:18, 1080.28it/s]\n",
      "  3%|2         | 590/20000 [00:00<00:15, 1249.62it/s]\n",
      "  4%|3         | 772/20000 [00:00<00:13, 1433.50it/s]\n",
      "  5%|4         | 984/20000 [00:00<00:11, 1647.47it/s]\n",
      "  6%|6         | 1233/20000 [00:00<00:09, 1904.59it/s]\n",
      "  7%|7         | 1476/20000 [00:01<00:08, 2059.94it/s]\n",
      "  9%|9         | 1809/20000 [00:01<00:07, 2443.58it/s]\n",
      " 11%|#         | 2157/20000 [00:01<00:06, 2757.34it/s]\n",
      " 13%|#3        | 2613/20000 [00:01<00:05, 3293.59it/s]\n",
      " 16%|#6        | 3256/20000 [00:01<00:03, 4231.88it/s]\n",
      " 19%|#8        | 3761/20000 [00:01<00:03, 4220.52it/s]\n",
      " 21%|##        | 4184/20000 [00:01<00:05, 2971.55it/s]\n",
      " 23%|##2       | 4531/20000 [00:01<00:05, 2590.02it/s]\n",
      " 24%|##4       | 4830/20000 [00:02<00:06, 2422.60it/s]\n",
      " 26%|##5       | 5100/20000 [00:02<00:06, 2340.61it/s]\n",
      " 27%|##6       | 5352/20000 [00:02<00:06, 2333.07it/s]\n",
      " 28%|##8       | 5601/20000 [00:02<00:06, 2366.05it/s]\n",
      " 29%|##9       | 5859/20000 [00:02<00:05, 2414.79it/s]\n",
      " 31%|###       | 6153/20000 [00:02<00:05, 2547.90it/s]\n",
      " 32%|###2      | 6463/20000 [00:02<00:05, 2697.69it/s]\n",
      " 34%|###4      | 6819/20000 [00:02<00:04, 2932.61it/s]\n",
      " 36%|###6      | 7244/20000 [00:02<00:03, 3296.50it/s]\n",
      " 39%|###8      | 7761/20000 [00:03<00:03, 3834.64it/s]\n",
      " 42%|####1     | 8328/20000 [00:03<00:03, 3771.92it/s]\n",
      " 44%|####3     | 8711/20000 [00:03<00:03, 2952.11it/s]\n",
      " 45%|####5     | 9035/20000 [00:03<00:04, 2630.37it/s]\n",
      " 47%|####6     | 9321/20000 [00:03<00:04, 2464.65it/s]\n",
      " 48%|####7     | 9583/20000 [00:03<00:04, 2429.43it/s]\n",
      " 49%|####9     | 9836/20000 [00:03<00:04, 2434.78it/s]\n",
      " 51%|#####     | 10101/20000 [00:04<00:03, 2488.27it/s]\n",
      " 52%|#####1    | 10391/20000 [00:04<00:03, 2596.34it/s]\n",
      " 54%|#####3    | 10704/20000 [00:04<00:03, 2742.28it/s]\n",
      " 55%|#####5    | 11077/20000 [00:04<00:02, 3012.11it/s]\n",
      " 58%|#####7    | 11501/20000 [00:04<00:02, 3360.26it/s]\n",
      " 60%|######    | 12045/20000 [00:04<00:02, 3956.22it/s]\n",
      " 63%|######3   | 12604/20000 [00:04<00:01, 3793.68it/s]\n",
      " 65%|######4   | 12992/20000 [00:04<00:02, 2838.61it/s]\n",
      " 67%|######6   | 13314/20000 [00:05<00:02, 2484.83it/s]\n",
      " 68%|######7   | 13593/20000 [00:05<00:02, 2343.40it/s]\n",
      " 69%|######9   | 13847/20000 [00:05<00:02, 2290.89it/s]\n",
      " 70%|#######   | 14089/20000 [00:05<00:02, 2283.04it/s]\n",
      " 72%|#######1  | 14340/20000 [00:05<00:02, 2333.27it/s]\n",
      " 73%|#######3  | 14601/20000 [00:05<00:02, 2404.18it/s]\n",
      " 74%|#######4  | 14864/20000 [00:05<00:02, 2463.43it/s]\n",
      " 76%|#######5  | 15146/20000 [00:05<00:01, 2558.60it/s]\n",
      " 77%|#######7  | 15464/20000 [00:05<00:01, 2733.87it/s]\n",
      " 79%|#######8  | 15790/20000 [00:06<00:01, 2881.17it/s]\n",
      " 81%|########1 | 16229/20000 [00:06<00:01, 3316.31it/s]\n",
      " 84%|########4 | 16861/20000 [00:06<00:00, 4196.58it/s]\n",
      " 86%|########6 | 17285/20000 [00:06<00:00, 3355.75it/s]\n",
      " 88%|########8 | 17651/20000 [00:06<00:01, 2318.57it/s]\n",
      " 90%|########9 | 17945/20000 [00:06<00:01, 1995.32it/s]\n",
      " 91%|######### | 18192/20000 [00:07<00:00, 1836.71it/s]\n",
      " 92%|#########2| 18408/20000 [00:07<00:00, 1746.90it/s]\n",
      " 93%|#########3| 18604/20000 [00:07<00:00, 1703.49it/s]\n",
      " 94%|#########3| 18788/20000 [00:07<00:00, 1685.93it/s]\n",
      " 95%|#########4| 18966/20000 [00:07<00:00, 1686.78it/s]\n",
      " 96%|#########5| 19141/20000 [00:07<00:00, 1700.99it/s]\n",
      " 97%|#########6| 19326/20000 [00:07<00:00, 1735.64it/s]\n",
      " 98%|#########7| 19521/20000 [00:07<00:00, 1789.12it/s]\n",
      " 99%|#########8| 19729/20000 [00:08<00:00, 1868.83it/s]\n",
      "100%|#########9| 19943/20000 [00:08<00:00, 1937.98it/s]\n",
      "100%|##########| 20000/20000 [00:08<00:00, 2451.69it/s]\n"
     ]
    }
   ],
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Lvk4q0-1N4x-"
   },
   "source": [
    "Dataset"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "zCXKEwspN4x_",
    "outputId": "3dcce6fa-932f-4b22-9dc8-398a4b402733",
    "ExecuteTime": {
     "end_time": "2025-02-05T02:00:03.866466Z",
     "start_time": "2025-02-05T01:55:59.689967Z"
    }
   },
   "source": [
    "from pathlib import Path\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "\n",
    "\n",
    "class LangPairDataset(Dataset):\n",
    "\n",
    "    def __init__(\n",
    "        self, mode=\"train\", max_length=128, overwrite_cache=False, data_dir=\"wmt16\",\n",
    "    ):\n",
    "        self.data_dir = Path(data_dir)\n",
    "        cache_path = self.data_dir / \".cache\" / f\"de2en_{mode}_{max_length}.npy\"\n",
    "\n",
    "        if overwrite_cache or not cache_path.exists():\n",
    "            cache_path.parent.mkdir(parents=True, exist_ok=True) # 创建缓存目录\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_src.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.src = file.readlines() # 读取源语言文件所有行\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_trg.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.trg = file.readlines() # 读取目标语言文件所有行\n",
    "\n",
    "            filtered_src = []\n",
    "            filtered_trg = []\n",
    "            # max length filter,超出最大长度的句子舍弃\n",
    "            for src, trg in zip(self.src, self.trg):\n",
    "                if len(src) <= max_length and len(trg) <= max_length: # 过滤长度超过最大长度的句子\n",
    "                    filtered_src.append(src.strip()) # 去掉句子前后的空格\n",
    "                    filtered_trg.append(trg.strip())\n",
    "            filtered_src = np.array(filtered_src)\n",
    "            filtered_trg = np.array(filtered_trg)\n",
    "            np.save(\n",
    "                cache_path,\n",
    "                {\"src\": filtered_src, \"trg\": filtered_trg },\n",
    "                allow_pickle=True,\n",
    "            )#allow_pickle=True允许保存对象数组，将过滤后的数据保存为 NumPy 数组，存储在缓存文件中\n",
    "            print(f\"save cache to {cache_path}\")\n",
    "\n",
    "        else:\n",
    "            cache_dict = np.load(cache_path, allow_pickle=True).item() #allow_pickle=True允许保存对象数组\n",
    "            print(f\"load {mode} dataset from {cache_path}\")\n",
    "            filtered_src = cache_dict[\"src\"]\n",
    "            filtered_trg = cache_dict[\"trg\"]\n",
    "\n",
    "        self.src = filtered_src\n",
    "        self.trg = filtered_trg\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.src[index], self.trg[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.src)\n",
    "\n",
    "\n",
    "train_ds = LangPairDataset(\"train\")\n",
    "val_ds = LangPairDataset(\"val\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "save cache to wmt16\\.cache\\de2en_train_128.npy\n",
      "save cache to wmt16\\.cache\\de2en_val_128.npy\n"
     ]
    }
   ],
   "execution_count": 3
  },
  {
   "cell_type": "code",
   "source": [
    "# !rm wmt16/.cache -r"
   ],
   "metadata": {
    "id": "yHB9TDpDQlv2",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:22.820353700Z",
     "start_time": "2024-08-05T08:06:22.812366100Z"
    }
   },
   "execution_count": 8,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "len(train_ds) #少了1000多个样本"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "meHHXL3MN4x_",
    "outputId": "a001c071-7021-4dff-8bc0-3e5da01d7210",
    "ExecuteTime": {
     "end_time": "2025-02-05T02:00:35.408288Z",
     "start_time": "2025-02-05T02:00:35.405012Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "27962"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "LZvJV37NN4x_",
    "outputId": "43909dbd-cf15-447b-e8b6-a07de381ad0a",
    "ExecuteTime": {
     "end_time": "2025-02-05T02:00:48.256360Z",
     "start_time": "2025-02-05T02:00:48.253937Z"
    }
   },
   "source": [
    "print(\"source: {}\\ntarget: {}\".format(*train_ds[-1]))"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "source: ein älterer mann sitzt mit einem jungen mit einem wagen vor einer fassade .\n",
      "target: an elderly man sits outside a storefront accompanied by a young boy with a cart .\n"
     ]
    }
   ],
   "execution_count": 5
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TE8gRzYQN4yA"
   },
   "source": [
    "### Tokenizer\n",
    "\n",
    "这里有两种处理方式，分别对应着 encoder 和 decoder 的 word embedding 是否共享，这里实现共享的方案"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 66,
     "referenced_widgets": [
      "7190c928edb040c3a523cdd2e3f45572",
      "990741ff083d457587665d8f620b69c3",
      "00fe6a550df547cea5b1e0d84c1a3f5f",
      "fab8b1e3187841ee894b92859fa4821b",
      "f9b0d916e1a64c2a9f384e42f3d5dcab",
      "a78199650ecc47bd81f482a2671b563a",
      "673bd3773a374261a01cfc48081c27f3",
      "83f345f7424c43929d7a304cbfb6d0fe",
      "58d69d086f644e89937347e1f9261d6f",
      "8cf462fa996c41b18fb4571e0a757363",
      "434ac4c51db74681942d2d3bd869b1df"
     ]
    },
    "id": "yAmlq_9YN4yA",
    "outputId": "263a89f7-9bee-4c6d-cf7a-7d82b0a81f92",
    "ExecuteTime": {
     "end_time": "2025-02-05T02:26:13.585154Z",
     "start_time": "2025-02-05T02:26:13.556653Z"
    }
   },
   "source": [
    "#载入词表，看下词表长度，词表就像英语字典,构建word2idx和idx2word\n",
    "word2idx = {\n",
    "    \"[PAD]\": 0,     # 填充 token\n",
    "    \"[BOS]\": 1,     # begin of sentence\n",
    "    \"[UNK]\": 2,     # 未知 token\n",
    "    \"[EOS]\": 3,     # end of sentence\n",
    "}\n",
    "idx2word = {value: key for key, value in word2idx.items()}\n",
    "index = len(idx2word)\n",
    "threshold = 1  # 出现次数低于此的token舍弃\n",
    "\n",
    "with open(\"wmt16/vocab\", \"r\", encoding=\"utf8\") as file:\n",
    "    for line in tqdm(file.readlines()):\n",
    "        token, counts = line.strip().split()\n",
    "        if int(counts) >= threshold:\n",
    "            word2idx[token] = index\n",
    "            idx2word[index] = token\n",
    "            index += 1\n",
    "\n",
    "vocab_size = len(word2idx)\n",
    "print(\"vocab_size: {}\".format(vocab_size))"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/18107 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "221cd7514e84484896313136414d5651"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab_size: 18111\n"
     ]
    }
   ],
   "execution_count": 6
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "e_ed0jmJN4yA",
    "outputId": "407a9e69-4981-42ae-c7a9-aa4c701185ef",
    "ExecuteTime": {
     "end_time": "2025-02-05T02:31:52.227873Z",
     "start_time": "2025-02-05T02:31:52.192959Z"
    }
   },
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=128, pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx\n",
    "        self.idx2word = idx2word\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.unk_idx = unk_idx\n",
    "\n",
    "    def encode(self, text_list, padding_first=False, add_bos=True, add_eos=True, return_mask=False):\n",
    "        \"\"\"如果padding_first == True，则padding加载前面，否则加载后面\"\"\"\n",
    "        max_length = min(self.max_length, add_eos + add_bos + max([len(text) for text in text_list]))\n",
    "        indices_list = []\n",
    "        for text in text_list:\n",
    "            indices = [self.word2idx.get(word, self.unk_idx) for word in text[:max_length - add_bos - add_eos]]\n",
    "            if add_bos:\n",
    "                indices = [self.bos_idx] + indices\n",
    "            if add_eos:\n",
    "                indices = indices + [self.eos_idx]\n",
    "            if padding_first:\n",
    "                indices = [self.pad_idx] * (max_length - len(indices)) + indices\n",
    "            else:\n",
    "                indices = indices + [self.pad_idx] * (max_length - len(indices))\n",
    "            indices_list.append(indices)\n",
    "        input_ids = torch.tensor(indices_list)\n",
    "        masks = (input_ids == self.pad_idx).to(dtype=torch.int64) # 为了方便损失计算，这里的mask为0的地方需要计算，为1的地方不需要计算\n",
    "        return input_ids if not return_mask else (input_ids, masks)\n",
    "\n",
    "\n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                word = self.idx2word.get(index, \"[UNK]\")\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":\n",
    "                    break\n",
    "                text.append(word)\n",
    "            text_list.append(\" \".join(text) if not split else text)\n",
    "        return text_list\n",
    "\n",
    "\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word)\n",
    "\n",
    "\n",
    "raw_text = [\"hello world\".split(), \"tokenize text datas with batch\".split(), \"this is a test\".split()]\n",
    "indices = tokenizer.encode(raw_text, padding_first=False, add_bos=True, add_eos=True)\n",
    "decode_text = tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "print(\"raw text\")\n",
    "for raw in raw_text:\n",
    "    print(raw)\n",
    "print(\"indices\")\n",
    "for index in indices:\n",
    "    print(index)\n",
    "print(\"decode text\")\n",
    "for decode in decode_text:\n",
    "    print(decode)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw text\n",
      "['hello', 'world']\n",
      "['tokenize', 'text', 'datas', 'with', 'batch']\n",
      "['this', 'is', 'a', 'test']\n",
      "indices\n",
      "tensor([   1, 9458, 3522,    3,    0,    0,    0])\n",
      "tensor([   1,    2, 5463,    2,   22,    2,    3])\n",
      "tensor([   1,  385,   18,    5, 5699,    3,    0])\n",
      "decode text\n",
      "[BOS] hello world [EOS] [PAD] [PAD] [PAD]\n",
      "[BOS] [UNK] text [UNK] with [UNK] [EOS]\n",
      "[BOS] this is a test [EOS] [PAD]\n"
     ]
    }
   ],
   "execution_count": 7
  },
  {
   "cell_type": "code",
   "source": [
    "for i,j in train_ds:\n",
    "    print(len(i))\n",
    "    print(len(j))\n",
    "    break"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-05T02:58:16.189200Z",
     "start_time": "2025-02-05T02:58:16.186003Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "66\n",
      "54\n"
     ]
    }
   ],
   "execution_count": 9
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ftcEnnKxN4yB"
   },
   "source": [
    "### Transformer Batch Sampler\n",
    "\n",
    "> Sentence pairs were batched together by approximate sequence length. Each training batch contained a set of sentence pairs containing approximately 25000 source tokens and 25000 target tokens\n",
    "句子按照序列长度差不多的分到一个批次。 每个训练批次包含一组句子对，其中包含大约 25000 个源标记和 25000 个目标标记"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "qP8nznIuN4yB",
    "ExecuteTime": {
     "end_time": "2025-02-05T03:09:05.430080Z",
     "start_time": "2025-02-05T03:09:05.425503Z"
    }
   },
   "source": [
    "class SampleInfo: #下面的info对象\n",
    "    def __init__(self, i, lens):\n",
    "        \"\"\"\n",
    "        记录文本对的序号和长度信息\n",
    "        输入：\n",
    "            - i (int): 文本对的序号。\n",
    "            - lens (list): 文本对源语言和目标语言的长度\n",
    "        \"\"\"\n",
    "        self.i = i\n",
    "        # 加一是考虑填补在文本前后的特殊词元，lens[0]和lens[1]分别表示源语言和目标语言的长度\n",
    "        self.max_len = max(lens[0], lens[1]) + 1\n",
    "        self.src_len = lens[0] + 1\n",
    "        self.trg_len = lens[1] + 1\n",
    "\n",
    "# 一个批量生成器，根据词元数目的限制来控制批量的大小。它会根据传入的样本信息，在不超过设定大小的情况下，逐步构建批量。\n",
    "class TokenBatchCreator:\n",
    "    def __init__(self, batch_size):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        batch_size (int): 用于限制批量的大小。\n",
    "        功能:\n",
    "        初始化了一个空的批量列表 _batch。\n",
    "        设定了初始的最大长度为 -1。\n",
    "        存储了传入的 batch_size。\n",
    "        \"\"\"\n",
    "\n",
    "        self._batch = []  #这个就是之前的batch_size，就是第一个batch内有多少个样本\n",
    "        self.max_len = -1\n",
    "        self._batch_size = batch_size # 限制批量的大小,假设是4096\n",
    "\n",
    "    def append(self, info: SampleInfo):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        info (SampleInfo): 文本对的信息。\n",
    "        功能:\n",
    "        接收一个 SampleInfo 对象，并根据其最大长度信息更新当前批量的最大长度。\n",
    "        如果将新的样本加入批量后超过了批量大小限制，它会返回已有的批量并将新的样本加入新的批量。\n",
    "        否则，它会更新最大长度并将样本添加到当前批量中。\n",
    "        \"\"\"\n",
    "        # 更新当前批量的最大长度\n",
    "        cur_len = info.max_len # 当前样本的长度\n",
    "        max_len = max(self.max_len, cur_len) # 每来一个样本，更新当前批次的最大长度\n",
    "        # 如果新的样本加入批量后超过大小限制，则将已有的批量返回，新的样本加入新的批量\n",
    "        if max_len * (len(self._batch) + 1) > self._batch_size:\n",
    "            self._batch, result = [], self._batch # 保存当前的batch，并返回,这里的result是之前的batch,_batch清空\n",
    "            self._batch.append(info) #箱子里的第一条样本，放入\n",
    "            self.max_len = cur_len #因为是当前batch的第一个样本，所以它的长度就是当前长度\n",
    "            return result\n",
    "        else:\n",
    "            self.max_len = max_len\n",
    "            self._batch.append(info) # 将样本添加到当前批量中\n",
    "            return None\n",
    "\n",
    "    @property\n",
    "    def batch(self):\n",
    "        return self._batch"
   ],
   "outputs": [],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "_Vtc0gXEN4yB",
    "ExecuteTime": {
     "end_time": "2025-02-05T03:10:29.416972Z",
     "start_time": "2025-02-05T03:10:29.410479Z"
    }
   },
   "source": [
    "from torch.utils.data import BatchSampler\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class TransformerBatchSampler(BatchSampler):\n",
    "    def __init__(self,\n",
    "                 dataset,\n",
    "                 batch_size,\n",
    "                 shuffle_batch=False,\n",
    "                 clip_last_batch=False,\n",
    "                 seed=0):\n",
    "        \"\"\"\n",
    "        批量采样器\n",
    "        输入:\n",
    "            - dataset: 数据集\n",
    "            - batch_size: 批量大小\n",
    "            - shuffle_batch: 是否对生成的批量进行洗牌\n",
    "            - clip_last_batch: 是否裁剪最后剩下的数据\n",
    "            - seed: 随机数种子\n",
    "        \"\"\"\n",
    "        self._dataset = dataset\n",
    "        self._batch_size = batch_size\n",
    "        self._shuffle_batch = shuffle_batch\n",
    "        self._clip_last_batch = clip_last_batch\n",
    "        self._seed = seed #下面3个是为了随机\n",
    "        self._random = np.random\n",
    "        self._random.seed(seed)\n",
    "\n",
    "        self._sample_infos = []\n",
    "        # 根据数据集中的每个样本，创建了对应的 SampleInfo 对象，包含了样本的索引和长度信息。\n",
    "        for i, data in enumerate(self._dataset):\n",
    "            lens = [len(data[0]), len(data[1])] #输入和输出的长度计算放到lens中\n",
    "            self._sample_infos.append(SampleInfo(i, lens))\n",
    "\n",
    "    def __iter__(self):\n",
    "        \"\"\"\n",
    "        对数据集中的样本进行排序，排序规则是先按源语言长度排序，如果相同则按目标语言长度排序。\n",
    "        使用 TokenBatchCreator 逐步组装批量数据，当满足批量大小时返回一个批量的样本信息。\n",
    "        如果不裁剪最后一个批次的数据且存在剩余样本，则将这些样本组成最后一个批次。\n",
    "        如果需要对批量进行洗牌，则对批次进行洗牌操作。\n",
    "        通过迭代器，抛出每个批量的样本在数据集中的索引。\n",
    "        \"\"\"\n",
    "        # 排序，如果源语言长度相同则按照目标语言的长度排列\n",
    "        infos = sorted(self._sample_infos,\n",
    "                       key=lambda x: (x.src_len, x.trg_len))\n",
    "        # 把样本放入到箱子里，所有装箱后的箱子，每一个箱子都放入batch_infos\n",
    "        batch_infos = []\n",
    "        batch_creator = TokenBatchCreator(self._batch_size) # 批量生成器\n",
    "        for info in infos:\n",
    "            batch = batch_creator.append(info)\n",
    "            # 存够一个batch的样本信息后，会把这个batch返回，否则返回为None\n",
    "            if batch is not None:\n",
    "                batch_infos.append(batch)\n",
    "\n",
    "        # 是否抛弃最后批量的文本对\n",
    "        if not self._clip_last_batch and len(batch_creator.batch) != 0:\n",
    "            batch_infos.append(batch_creator.batch) # 最后一个batch\n",
    "\n",
    "        # 打乱batch，打乱的是箱子的顺序\n",
    "        if self._shuffle_batch:\n",
    "            self._random.shuffle(batch_infos)\n",
    "\n",
    "        self.batch_number = len(batch_infos)\n",
    "        # print(self.batch_number) #为了理解\n",
    "\n",
    "        # 抛出一个箱子里所有样本的序号\n",
    "        for batch in batch_infos:\n",
    "            batch_indices = [info.i for info in batch] # 批量的样本在数据集中的索引，第一个batch[0,1,.....82]，第二个batch[83,84,85,86,87]\n",
    "            yield batch_indices\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"\n",
    "        返回批量的数量\n",
    "        \"\"\"\n",
    "        if hasattr(self, \"batch_number\"):\n",
    "            return self.batch_number\n",
    "        # 计算批量的数量,没有用到下面的情况，不用看\n",
    "        batch_number = (len(self._dataset) +\n",
    "                        self._batch_size) // self._batch_size\n",
    "        return batch_number"
   ],
   "outputs": [],
   "execution_count": 16
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "d00AFRzVN4yC",
    "outputId": "a43c1a14-102f-4cbb-fff1-761cb46e31b3",
    "ExecuteTime": {
     "end_time": "2025-02-05T03:10:31.201945Z",
     "start_time": "2025-02-05T03:10:31.162651Z"
    }
   },
   "source": [
    "sampler = TransformerBatchSampler(train_ds, batch_size=4096, shuffle_batch=True)\n",
    "\n",
    "#为什么这里每个批量的样本对数目不一样呢？长度*batch_number>4096的时候，就会返回上一个batch，然后新的样本加入新的batch,具体要看TokenBatchCreator的44行"
   ],
   "outputs": [],
   "execution_count": 17
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-05T03:09:12.123625Z",
     "start_time": "2025-02-05T03:09:12.016972Z"
    }
   },
   "cell_type": "code",
   "source": [
    "for i in sampler:\n",
    "    print(i)\n",
    "    break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[26845, 982, 1864, 3222, 4716, 5341, 5897, 6103, 8531, 10067, 11125, 13063, 14929, 15158, 18063, 18502, 20661, 20986, 21320, 22030, 22882, 23567, 24909, 1881, 2268, 4268, 7413, 10166, 12447, 13573, 15332, 16727, 17657, 20829, 21508, 23094, 24274, 25801, 518, 2145, 4111, 4739, 4745, 7493, 7623, 8369, 8618, 8730, 9054, 9391, 9808, 10141, 10412, 11422, 13534, 15038]\n"
     ]
    }
   ],
   "execution_count": 14
  },
  {
   "cell_type": "code",
   "source": [
    "for idx, batch in enumerate(sampler):\n",
    "    print(\"第{}批量的数据中含有文本对是：{}，数量为：{}\".format(idx, batch, len(batch)))\n",
    "    if idx >= 3:\n",
    "        break"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-05T03:15:30.298678Z",
     "start_time": "2025-02-05T03:15:30.278251Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第0批量的数据中含有文本对是：[16049, 16193, 8575, 8984, 27828, 10852, 15294, 16913, 1032, 18596, 5478, 8237, 9337, 12510, 4154, 16465, 21426, 7984, 2865, 4740, 25456, 17136, 303, 9778, 15546, 13972, 5784, 5782, 15117, 11577, 7710, 15671, 17074, 21701, 9447, 22717, 16728, 19257, 24219, 25845, 13079, 1744, 2887, 5320, 5587, 26541, 4651, 21146, 25472, 55, 6030, 7851, 11656, 15782, 17844, 18660, 9972, 18189, 298, 15029, 13559, 3413, 6790, 18639, 3862, 12074, 13781, 9741, 13334, 14503, 1186, 18932, 7052, 14447]，数量为：74\n",
      "第1批量的数据中含有文本对是：[18520, 21375, 27523, 361, 11097, 9031, 24310, 267, 983, 1901, 3319, 14810, 6493, 12906, 1668, 13625, 13439, 1899, 14149, 15160, 15623, 16750, 25999, 26931, 20677, 12889, 23036, 2480, 6124, 12241, 1091, 11054, 18679, 21963, 27684, 11600, 1525, 3198, 5413, 24619, 21654, 136, 3133, 10045, 12096, 14211, 17046, 13892, 3509, 24992, 6215, 27582, 5175, 11436, 4387, 7440, 7698, 13819, 13920, 15976, 21420, 24368, 1613, 2094, 6155, 10556, 13796, 19752, 27306, 11012, 23205, 14284, 20693, 24477, 3443, 11711, 13985, 8607, 10607, 21681, 9582, 14107, 17433, 26766, 3940, 22858, 16769]，数量为：87\n",
      "第2批量的数据中含有文本对是：[23980, 12478, 17102, 11048, 21854, 5135, 3033, 5438, 4189, 8219, 1878, 9060, 4353, 4629, 9057, 11918, 16314, 4400, 6329, 9120, 1614, 1470, 1853, 7169, 7828, 3696, 10425, 13989, 26511, 4056, 6134, 10561, 1089, 6883, 7460, 24871, 17592, 16596, 22741, 23773, 18522, 27334, 3731, 14901, 24553, 21070, 15097, 9198, 12439, 18995, 14367, 25178, 12226, 13040, 14870, 15690, 354, 927, 2992, 5379, 6009, 9667, 14768, 19978, 24585, 475, 1691, 8657, 12758, 16550, 3263, 4084, 8496, 9469, 16388, 2821, 9823, 17658, 18384, 22921, 1509, 7732, 2119, 7326, 10935]，数量为：85\n",
      "第3批量的数据中含有文本对是：[13542, 16849, 23409, 9934, 12980, 5678, 9578, 15075, 1477, 3527, 13172, 22621, 24183, 26988, 2076, 5572, 4684, 15434, 25466, 17730, 5333, 7682, 13380, 16463, 6941, 9527, 16519, 19358, 14759, 638, 13119, 9122, 11429, 21540, 7096, 8201, 12518, 9070, 12389, 20832, 8643, 12159, 17947, 20711, 8553, 21863, 22250, 5928, 6756, 10061, 14257, 19932, 10032, 12661, 15127, 16491, 20175, 25323, 27678, 431, 3223, 4351, 4604, 4816, 7387, 8866, 8960, 11404, 12351, 16228, 110]，数量为：71\n"
     ]
    }
   ],
   "execution_count": 24
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "outputs": [
    {
     "data": {
      "text/plain": "533"
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(sampler)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:23.319151500Z",
     "start_time": "2024-08-05T08:06:23.302665100Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Olkaw4JNN4yC"
   },
   "source": [
    "### DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "rvvuNJIzN4yC",
    "ExecuteTime": {
     "end_time": "2025-02-05T03:23:39.451799Z",
     "start_time": "2025-02-05T03:23:39.447798Z"
    }
   },
   "source": [
    "def collate_fct(batch, tokenizer):\n",
    "    src_words = [pair[0].split() for pair in batch]\n",
    "    trg_words = [pair[1].split() for pair in batch]\n",
    "\n",
    "    # [BOS] src [EOS] [PAD]\n",
    "    encoder_inputs, encoder_inputs_mask = tokenizer.encode(\n",
    "        src_words, padding_first=False, add_bos=True, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    # [BOS] trg [PAD]\n",
    "    decoder_inputs = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=True, add_eos=False, return_mask=False,\n",
    "        )\n",
    "\n",
    "    # trg [EOS] [PAD]\n",
    "    decoder_labels, decoder_labels_mask = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=False, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    return {\n",
    "        \"encoder_inputs\": encoder_inputs.to(device=device),\n",
    "        \"encoder_inputs_mask\": encoder_inputs_mask.to(device=device),\n",
    "        \"decoder_inputs\": decoder_inputs.to(device=device),\n",
    "        \"decoder_labels\": decoder_labels.to(device=device),\n",
    "        \"decoder_labels_mask\": decoder_labels_mask.to(device=device),\n",
    "    }\n"
   ],
   "outputs": [],
   "execution_count": 25
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "5p79gPo5N4yC",
    "outputId": "2b1d43ae-a2ed-4333-8274-50c62c2fc6c9",
    "ExecuteTime": {
     "end_time": "2025-02-05T06:05:12.419840Z",
     "start_time": "2025-02-05T06:05:12.297969Z"
    }
   },
   "source": [
    "from functools import partial # 固定collate_fct的tokenizer参数\n",
    "\n",
    "#可以调大batch_size,来看最终的bleu，如果GPU内存不够，可以减小batch_size\n",
    "sampler = TransformerBatchSampler(train_ds, batch_size=256, shuffle_batch=True)\n",
    "# https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader\n",
    "sample_dl = DataLoader(train_ds, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer)) #partial函数，固定collate_fct的tokenizer参数\n",
    "\n",
    "for batch in sample_dl:#外层是拿每个batch\n",
    "    for key, value in batch.items():#内层是拿每个batch里面是一个字典\n",
    "        print(key)\n",
    "        print(value)\n",
    "    break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "encoder_inputs\n",
      "tensor([[   1, 9771, 9771,    3,    0,    0],\n",
      "        [   1, 9771, 9771,    3,    0,    0],\n",
      "        [   1,    7,   17, 9015,    4,    3],\n",
      "        [   1,   25,   74, 2571,    4,    3]])\n",
      "encoder_inputs_mask\n",
      "tensor([[0, 0, 0, 0, 1, 1],\n",
      "        [0, 0, 0, 0, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0]])\n",
      "decoder_inputs\n",
      "tensor([[    1,    75,  8631,   471,   300,  2038,  5790,   216,  1354, 10332,\n",
      "             4],\n",
      "        [    1,    38,   193, 10092,  3577, 10479,   130,  7302,  4187,     4,\n",
      "             0],\n",
      "        [    1,     5,    16,    18,   305,     5,  6510,     4,     0,     0,\n",
      "             0],\n",
      "        [    1,    26,   210,    18,    63,     5,  2571,     4,     0,     0,\n",
      "             0]])\n",
      "decoder_labels\n",
      "tensor([[   75,  8631,   471,   300,  2038,  5790,   216,  1354, 10332,     4,\n",
      "             3],\n",
      "        [   38,   193, 10092,  3577, 10479,   130,  7302,  4187,     4,     3,\n",
      "             0],\n",
      "        [    5,    16,    18,   305,     5,  6510,     4,     3,     0,     0,\n",
      "             0],\n",
      "        [   26,   210,    18,    63,     5,  2571,     4,     3,     0,     0,\n",
      "             0]])\n",
      "decoder_labels_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]])\n"
     ]
    }
   ],
   "execution_count": 26
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0AadtoM3N4yC"
   },
   "source": [
    "## 定义模型\n",
    "\n",
    "- Transformer模型由Embedding、Transformer-Block组成\n",
    "- Embedding包括：\n",
    "    - WordEmbedding\n",
    "    - PositionEmbedding\n",
    "- Transformer-Block包括：\n",
    "    - Self-Attention\n",
    "    - Cross-Attention\n",
    "    - MLP"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VTl-sSJmN4yD"
   },
   "source": [
    "### Embedding"
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-05T06:19:25.113017Z",
     "start_time": "2025-02-05T06:19:25.108465Z"
    }
   },
   "cell_type": "code",
   "source": "torch.arange(0, 128).unsqueeze(1).shape",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([128, 1])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 29
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 455
    },
    "id": "y66CxrsBN4yD",
    "outputId": "c703025c-afc5-4012-d8e3-0e9d4ad253ec",
    "ExecuteTime": {
     "end_time": "2025-02-05T06:42:37.849930Z",
     "start_time": "2025-02-05T06:42:37.755745Z"
    }
   },
   "source": [
    "\n",
    "class TransformerEmbedding(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.hidden_size = config[\"d_model\"] # 词向量维度\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "\n",
    "        # layers,设置padding_idx可以让pad的词向量全为0\n",
    "        self.word_embedding = nn.Embedding(\n",
    "            self.vocab_size, self.hidden_size, padding_idx=self.pad_idx\n",
    "        )\n",
    "        self.pos_embedding = nn.Embedding(\n",
    "            self.max_length,\n",
    "            self.hidden_size,\n",
    "            _weight=self.get_positional_encoding(\n",
    "                self.max_length, self.hidden_size\n",
    "            ),# 位置编码，权重通过get_positional_encoding函数计算得到\n",
    "        )\n",
    "        self.pos_embedding.weight.requires_grad_(False) # 不更新位置编码的权重\n",
    "        self.dropout = nn.Dropout(dropout_rate) # 随机失活层\n",
    "\n",
    "    def get_word_embedding_weights(self):\n",
    "        return self.word_embedding.weight\n",
    "\n",
    "    # 计算位置信息\n",
    "    @classmethod\n",
    "    def get_positional_encoding(self, max_length, hidden_size):#max_length是最大长度，hidden_size是embedding维度相等\n",
    "        # Compute the positional encodings once in log space.\n",
    "        pe = torch.zeros(max_length, hidden_size) # 初始化位置编码\n",
    "        # .unsqueeze(1) 是将这个一维张量转换为二维张量，即将其形状从 (max_length,) 变为 (max_length, 1)。这个操作在张量的维度上增加了一个维度，使其从一维变为二维，第二维的大小为 1。\n",
    "        position = torch.arange(0, max_length).unsqueeze(1) # 位置信息,从0到max_length-1\n",
    "        div_term = torch.exp(\n",
    "            torch.arange(0, hidden_size, 2)\n",
    "            * -(torch.log(torch.Tensor([10000.0])) / hidden_size)\n",
    "        )# 计算位置编码的权重,为了性能考量（是数学上的对数函数分解）\n",
    "        pe[:, 0::2] = torch.sin(position * div_term)\n",
    "        pe[:, 1::2] = torch.cos(position * div_term)\n",
    "        return pe\n",
    "\n",
    "    def forward(self, input_ids):\n",
    "        # input_ids: [batch_size, seq_len]\n",
    "        seq_len = input_ids.shape[1]\n",
    "        assert (\n",
    "            seq_len <= self.max_length\n",
    "        ), f\"input sequence length should no more than {self.max_length} but got {seq_len}\"\n",
    "\n",
    "        position_ids = torch.arange(seq_len, dtype=torch.long, device=input_ids.device)\n",
    "        position_ids = position_ids.unsqueeze(0).expand_as(input_ids)\n",
    "        print(position_ids) #为了调试\n",
    "        # embedding\n",
    "        word_embeds = self.word_embedding(input_ids) # 词嵌入\n",
    "        pos_embeds = self.pos_embedding(position_ids) # 位置编码\n",
    "        embeds = word_embeds + pos_embeds\n",
    "        embeds = self.dropout(embeds)\n",
    "\n",
    "        return embeds\n",
    "\n",
    "\n",
    "def plot_position_embedding(position_embedding):# 绘制位置编码\n",
    "    plt.pcolormesh(position_embedding) # 绘制位置编码矩阵\n",
    "    plt.xlabel('Depth')\n",
    "    plt.ylabel('Position')\n",
    "    plt.colorbar() # 颜色条，-1到1的颜色范围\n",
    "    plt.show()\n",
    "\n",
    "position_embedding = TransformerEmbedding.get_positional_encoding(64, 128)\n",
    "plot_position_embedding(position_embedding)\n"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 40
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-05T06:26:02.642565Z",
     "start_time": "2025-02-05T06:26:02.638419Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import math\n",
    "#为了理解指数对数变换\n",
    "def positional_encoding_weights(emb_size):\n",
    "    # 计算底数和指数\n",
    "    base = math.log(10000)\n",
    "    exponent = -5 / emb_size\n",
    "    \n",
    "    # 应用指数函数\n",
    "    weight = math.exp(exponent*base) \n",
    "    \n",
    "    return weight\n",
    "\n",
    "emb_size = 10  # 举例，可以替换成你需要的大小\n",
    "weight = positional_encoding_weights(emb_size)\n",
    "print(weight)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.009999999999999995\n"
     ]
    }
   ],
   "execution_count": 32
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-05T06:27:52.403574Z",
     "start_time": "2025-02-05T06:27:52.399253Z"
    }
   },
   "cell_type": "code",
   "source": "1/10000**(1/2)",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.01"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 36
  },
  {
   "cell_type": "code",
   "source": [
    "#随机input，调用TransformerEmbedding\n",
    "config={\n",
    "    \"vocab_size\": 100,\n",
    "    \"d_model\": 128,\n",
    "    \"pad_idx\": 0,\n",
    "    \"max_length\": 64,\n",
    "    \"dropout\": 0.1,\n",
    "}\n",
    "input_ids = torch.randint(0, 100, (2, 50))\n",
    "embeds = TransformerEmbedding(config)(input_ids)\n",
    "embeds.shape"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-05T06:44:39.205609Z",
     "start_time": "2025-02-05T06:44:39.199098Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,\n",
      "         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,\n",
      "         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49],\n",
      "        [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,\n",
      "         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,\n",
      "         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "torch.Size([2, 50, 128])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 41
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wXavO0SNN4yD"
   },
   "source": [
    "### Transformer Block"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HZL4mu3pN4yD"
   },
   "source": [
    "#### scaled-dot-product-attention"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 970
    },
    "id": "U9QwdVQYN4yD",
    "outputId": "b6ae9ac8-c71c-4a7b-fd4b-9c15e92dba2c",
    "ExecuteTime": {
     "end_time": "2025-02-05T07:53:39.793343Z",
     "start_time": "2025-02-05T07:53:39.752135Z"
    }
   },
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Optional, Tuple\n",
    "\n",
    "Tensor = torch.Tensor\n",
    "\n",
    "@dataclass\n",
    "class AttentionOutput:\n",
    "    hidden_states: Tensor\n",
    "    attn_scores: Tensor\n",
    "\n",
    "class MultiHeadAttention(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"] # 隐藏层大小\n",
    "        self.num_heads = config[\"num_heads\"] # 多头注意力的头数\n",
    "        assert (\n",
    "            self.hidden_size % self.num_heads == 0\n",
    "        ), \"Hidden size must be divisible by num_heads but got {} and {}\".format(\n",
    "            self.hidden_size, self.num_heads\n",
    "        )\n",
    "        self.head_dim = self.hidden_size // self.num_heads # 每个头的维度\n",
    "\n",
    "        # layers\n",
    "        self.Wq = nn.Linear(self.hidden_size, self.hidden_size, bias=False) #第二个self.hidden_size可以*系数\n",
    "        self.Wk = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wv = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wo = nn.Linear(self.hidden_size, self.hidden_size, bias=False) # 输出层\n",
    "\n",
    "    def _split_heads(self, x: Tensor) -> Tensor:\n",
    "        bs, seq_len, _ = x.shape #假设输入的维度是[batch_size, seq_len, hidden_size],hidden_size是512\n",
    "        x = x.view(bs, seq_len, self.num_heads, self.head_dim) #num_heads是8，head_dim是64\n",
    "        return x.permute(0, 2, 1, 3) #变换维度，[batch_size, num_heads, seq_len, head_dim]\n",
    "\n",
    "    def _merge_heads(self, x: Tensor) -> Tensor:#将多头注意力的输出合并为一个张量\n",
    "        bs, _, seq_len, _ = x.shape #假设输入的维度是[batch_size, num_heads, seq_len, head_dim]\n",
    "        return x.permute(0, 2, 1, 3).reshape(bs, seq_len, self.hidden_size) # 变换维度，变为[batch_size, seq_len, hidden_size]\n",
    "\n",
    "    def forward(self, querys, keys, values, attn_mask=None) -> AttentionOutput:\n",
    "        # split heads\n",
    "        querys = self._split_heads(self.Wq(querys)) #(batch_size, seq_len,hidden_dim)-->[batch_size, num_heads, seq_len, head_dim]\n",
    "        keys = self._split_heads(self.Wk(keys))#[batch_size, num_heads, seq_len, head_dim]\n",
    "        values = self._split_heads(self.Wv(values))#[batch_size, num_heads, seq_len, head_dim]\n",
    "\n",
    "        # calculate attention scores\n",
    "        qk_logits = torch.matmul(querys, keys.mT) # 计算注意力分数，matmul是矩阵乘法，mT是矩阵转置,qk_logits是[batch_size, num_heads, seq_len, seq_len]\n",
    "        # print(querys.shape[-2], keys.shape[-2])  #3 4\n",
    "        if attn_mask is not None:\n",
    "            attn_mask = attn_mask[:, :, : querys.shape[-2], : keys.shape[-2]]\n",
    "            qk_logits += attn_mask * -1e9 # 给需要mask的地方设置一个负无穷\n",
    "        attn_scores = F.softmax(qk_logits / (self.head_dim**0.5), dim=-1) # 计算注意力分数\n",
    "\n",
    "        # embeds的尺寸是[batch_size, num_heads, seq_len, head_dim]\n",
    "        embeds = torch.matmul(attn_scores, values) # softmax后的结果与value相乘，得到新的表示\n",
    "        embeds = self.Wo(self._merge_heads(embeds)) # 输出层 [batch_size, seq_len, hidden_size]\n",
    "\n",
    "        return AttentionOutput(hidden_states=embeds, attn_scores=attn_scores)\n",
    "\n",
    "mha = MultiHeadAttention({\"num_heads\": 2, \"d_model\": 2})\n",
    "query = torch.randn(2, 3, 2) # [batch_size, seq_len, hidden_size]\n",
    "query /= query.norm(dim=-1, keepdim=True) # 归一化\n",
    "key_value = torch.randn(2, 4, 2)\n",
    "print(f'key_value.shape {key_value.shape}')\n",
    "outputs = mha(query, key_value, key_value) #最终输出shape和query的shape一样\n",
    "print(outputs.hidden_states.shape)\n",
    "print(outputs.attn_scores.shape)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "key_value.shape torch.Size([2, 4, 2])\n",
      "torch.Size([2, 3, 2])\n",
      "torch.Size([2, 2, 3, 4])\n"
     ]
    }
   ],
   "execution_count": 44
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-05T07:25:40.806461Z",
     "start_time": "2025-02-05T07:25:40.801843Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#随机一个2阶张量，在-1维度计算softmax\n",
    "import torch.nn.functional as F\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "x = torch.randn(2, 3)\n",
    "x_softmax = F.softmax(x, dim=-1)\n",
    "print(x_softmax)\n",
    "\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.0771, 0.8014, 0.1214],\n",
      "        [0.2675, 0.5823, 0.1503]])\n"
     ]
    }
   ],
   "execution_count": 43
  },
  {
   "cell_type": "code",
   "source": [
    "# plt.subplots() 用于创建子图网格，其维度基于 outputs.attn_scores.shape[:2]。子图的行数和列数似乎由 outputs.attn_scores 的前两个维度确定。\n",
    "fig, axis = plt.subplots(*outputs.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs.attn_scores.shape[1]):\n",
    "        # axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())：此行使用 Matplotlib 的 matshow 绘制每个 i 和 j 的注意力分数热图。detach().numpy() 将 PyTorch 张量转换为 NumPy 数组以进行可视化。\n",
    "        axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs.attn_scores.shape[2]):\n",
    "            for y in range(outputs.attn_scores.shape[3]):\n",
    "                # axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")：此代码在热图上叠加文本，显示 (x, y) 位置处的注意力分数。格式化部分 f\"{outputs.attn_scores[i, j, x, y]:.2f}\" 确保以两位小数显示注意力分数。文本以白色居中显示在 (y, x) 坐标处。\n",
    "                axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention without mask\")\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-05T07:59:03.529749Z",
     "start_time": "2025-02-05T07:59:03.134589Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 46
  },
  {
   "cell_type": "code",
   "source": [
    "print('-'*50)\n",
    "# mask\n",
    "mask = torch.Tensor([[0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]]).reshape(1, 1, 3, 4) #手工构造mask\n",
    "outputs_masked = mha(query, key_value, key_value, mask)\n",
    "\n",
    "fig, axis = plt.subplots(*outputs_masked.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs_masked.attn_scores.shape[1]):\n",
    "        axis[i, j].matshow(outputs_masked.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs_masked.attn_scores.shape[2]):\n",
    "            for y in range(outputs_masked.attn_scores.shape[3]):\n",
    "                axis[i, j].text(y, x, f\"{outputs_masked.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention with mask\")\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-05T08:00:48.040777Z",
     "start_time": "2025-02-05T08:00:47.878351Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ],
      "image/png": "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1W5F6kIcwGZ5SeHfnv3/E/80dpTHjda/z4GpjSWM0fluv8OYMW78N+LGP+qoOXPmTPkgIrobjB1Exq323emEiIiI6iQmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgzGMisLVNgYmUFo9QgF8bs6YtHYcz2tbc19CJQNVQHnaCyNM640SJ3EoyZ+45MGDO7IeGGXoRagS0VREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCCYVREREpAgmFURERKQIJhVERESkCDPUMU926oin/fzgbmuLK4mJeGffflyIi9NZdkL79hjbpjVauLnJ50Hx8fj0yNFbyjdzccFrffugu7c3TE1MEJqcjOf/2oKYzEzo25PNffF06x5wt7LDlbR4vHN6Fy6kxOgsO9i7JZ5v0wuN7ZxhZmKC65mpWBYSiE3Xg7TKTGzeBe2c68HZ0gbDdyyT8zWULi4j0N11HOzMnJGQF4Fdcd8hNvfqHd/X2qEvRjd8HVczArA+6j3N9N7uj6ONY1/Ym7ujWF2IuNxQHEr4BTG5ITCEUc8PxvhXR8GlnhPCzt/AkpeWI+RkaLXl+z7cA5PefRT1fNwRfS0Oy15fhRPbz2qVmbRgAoZOHwg7J1tcOhqMxc//iOhQ3fs8VW9C746YfL8v3OxtcTUmEQvX70dQpO5jYVyPdhjZtQ2a13eVzy9HJWDxtiOa8uJ4mzm8J/q0bgJvV0dk5uXj+NVIfLXlCBIzsmEITzT3xfRW/prY8e6ZndXGjgcbtMRzMna4lMWOFCwPOY5NNy5qyoROeFPnez86t0fGGX0b19Afj/v0hYuFPUKzYvHFlc24nHHzju8bVK8j3uswEQcTLuH1c79opvfzaIsx3j3QyqEBHC1s8VTAV7iWGQtDGWUkseOuWyoOHTqE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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 47
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GkzzrRWVN4yE"
   },
   "source": [
    "#### Transformer-Block"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "b16xpL47N4yE",
    "ExecuteTime": {
     "end_time": "2025-02-05T08:14:03.533583Z",
     "start_time": "2025-02-05T08:14:03.525788Z"
    }
   },
   "source": [
    "# 通过使用 @dataclass 装饰器，Python 会自动为该类生成一些方法，如 __init__()、__repr__() 和 __eq__() 等，这些方法可以使类的使用更加方便。\n",
    "@dataclass\n",
    "class TransformerBlockOutput:\n",
    "# hidden_states: Tensor：用于存储某个块产生的隐藏状态。\n",
    "# self_attn_scores: Tensor：包含了自注意力机制（self-attention）所计算得到的注意力分数。\n",
    "# cross_attn_scores: Optional[Tensor] = None：是一个可选字段，存储了交叉注意力（cross-attention）计算得到的注意力分数。这里的 Optional 表示这个字段可以是 Tensor 类型，也可以是 None。\n",
    "    hidden_states: Tensor\n",
    "    self_attn_scores: Tensor\n",
    "    cross_attn_scores: Optional[Tensor] = None\n",
    "\n",
    "class TransformerBlock(nn.Module):\n",
    "    def __init__(self, config, add_cross_attention=False):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_heads = config[\"num_heads\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        ffn_dim = config[\"dim_feedforward\"]\n",
    "        eps = config[\"layer_norm_eps\"] # 层归一化的epsilon值\n",
    "\n",
    "        # self-attention\n",
    "        self.self_atten = MultiHeadAttention(config) # 多头注意力\n",
    "        self.self_ln = nn.LayerNorm(self.hidden_size, eps=eps) #层归一化(层标准化)\n",
    "        self.self_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "        # cross-attention，交叉注意力，decoder中使用,因此额外做一个判断\n",
    "        if add_cross_attention:\n",
    "            self.cross_atten = MultiHeadAttention(config)\n",
    "            self.cross_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "            self.cross_dropout = nn.Dropout(dropout_rate)\n",
    "        else:\n",
    "            self.cross_atten = None\n",
    "\n",
    "        # FFN,前馈神经网络\n",
    "        self.ffn = nn.Sequential(\n",
    "            nn.Linear(self.hidden_size, ffn_dim),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(ffn_dim, self.hidden_size),\n",
    "        )\n",
    "        self.ffn_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "        self.ffn_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        hidden_states,\n",
    "        attn_mask=None,\n",
    "        encoder_outputs=None,\n",
    "        cross_attn_mask=None,\n",
    "    ):\n",
    "        # self-attention,自注意力\n",
    "        self_atten_output = self.self_atten(\n",
    "            hidden_states, hidden_states, hidden_states, attn_mask\n",
    "        )\n",
    "        self_embeds = self.self_ln(\n",
    "            hidden_states + self.self_dropout(self_atten_output.hidden_states)\n",
    "        ) #多头注意力进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        # cross-attention，交叉注意力\n",
    "        if self.cross_atten is not None:\n",
    "            assert encoder_outputs is not None\n",
    "            cross_atten_output = self.cross_atten(\n",
    "                self_embeds, encoder_outputs, encoder_outputs, cross_attn_mask\n",
    "            ) #query是self_embeds，key和value都是encoder_outputs\n",
    "            cross_embeds = self.cross_ln(\n",
    "                self_embeds + self.cross_dropout(cross_atten_output.hidden_states)\n",
    "            ) # 交叉注意力进行dropout，然后和self_embeds进行残差连接，然后进行层归一化\n",
    "\n",
    "        # FFN\n",
    "        embeds = cross_embeds if self.cross_atten is not None else self_embeds # 如果有交叉注意力，则使用交叉注意力的输出作为FFN的输入；否则，使用self_embeds作为FFN的输入\n",
    "        ffn_output = self.ffn(embeds) # 前馈神经网络\n",
    "        embeds = self.ffn_ln(embeds + self.ffn_dropout(ffn_output)) # 前馈神经网络进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        return TransformerBlockOutput(\n",
    "            hidden_states=embeds,\n",
    "            self_attn_scores=self_atten_output.attn_scores,\n",
    "            cross_attn_scores=cross_atten_output.attn_scores\n",
    "            if self.cross_atten is not None\n",
    "            else None,\n",
    "        )"
   ],
   "outputs": [],
   "execution_count": 48
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "tfJIGaohN4yE"
   },
   "source": [
    "#### Encoder"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "sTLabHm7N4yE",
    "ExecuteTime": {
     "end_time": "2025-02-05T08:21:47.956551Z",
     "start_time": "2025-02-05T08:21:47.948706Z"
    }
   },
   "source": [
    "from typing import List\n",
    "\n",
    "@dataclass\n",
    "class TransformerEncoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    attn_scores: List[Tensor]\n",
    "\n",
    "# https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module\n",
    "class TransformerEncoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_encoder_layers\"]\n",
    "\n",
    "        # layers,仅仅是一个模块的列表，它本身没有定义前向传递（forward pass）过程。你需要在 forward 方法中明确地定义如何使用这些模块。\n",
    "        self.layers = nn.ModuleList(\n",
    "            [TransformerBlock(config) for _ in range(self.num_layers)]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "        self, encoder_inputs_embeds, attn_mask=None\n",
    "    ) -> TransformerEncoderOutput:\n",
    "        attn_scores = [] # 存储每个层的注意力分数\n",
    "        embeds = encoder_inputs_embeds # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        for layer in self.layers:\n",
    "            block_outputs = layer(embeds, attn_mask=attn_mask)\n",
    "            embeds = block_outputs.hidden_states #上一层的输出作为下一层的输入\n",
    "            # 在每个层的输出中，提取了隐藏状态 block_outputs.hidden_states，并将对应的注意力分数 block_outputs.self_attn_scores 添加到列表 attn_scores 中。\n",
    "            attn_scores.append(block_outputs.self_attn_scores) # 存储每个层的注意力分数,用于画图\n",
    "\n",
    "        return TransformerEncoderOutput(\n",
    "            last_hidden_states=embeds, attn_scores=attn_scores\n",
    "        )\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 49
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "BEMNj6eBN4yE"
   },
   "source": [
    "#### Decoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "id": "wCHOur-QN4yE",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:25.527550400Z",
     "start_time": "2024-08-05T08:06:25.525550800Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerDecoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    self_attn_scores: List[Tensor]\n",
    "    cross_attn_scores: List[Tensor]\n",
    "\n",
    "\n",
    "class TransformerDecoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_decoder_layers\"]\n",
    "\n",
    "        # layers\n",
    "        self.layers = nn.ModuleList(\n",
    "            [\n",
    "                TransformerBlock(config, add_cross_attention=True)\n",
    "                for _ in range(self.num_layers)\n",
    "            ]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        decoder_inputs_embeds,\n",
    "        encoder_outputs,\n",
    "        attn_mask=None,\n",
    "        cross_attn_mask=None,\n",
    "    ) -> TransformerDecoderOutput:\n",
    "        self_attn_scores = [] # 存储每个层的自注意力分数\n",
    "        cross_attn_scores = [] # 存储每个层的交叉注意力分数\n",
    "        embeds = decoder_inputs_embeds # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        for layer in self.layers:\n",
    "            block_outputs = layer(\n",
    "                embeds,\n",
    "                attn_mask=attn_mask, # 自注意力的mask\n",
    "                encoder_outputs=encoder_outputs,\n",
    "                cross_attn_mask=cross_attn_mask, # 交叉注意力的mask\n",
    "            )\n",
    "            embeds = block_outputs.hidden_states # 上一层的输出作为下一层的输入\n",
    "            self_attn_scores.append(block_outputs.self_attn_scores) # 存储每个层的自注意力分数\n",
    "            cross_attn_scores.append(block_outputs.cross_attn_scores) # 存储每个层的交叉注意力分数\n",
    "\n",
    "        return TransformerDecoderOutput(\n",
    "            last_hidden_states=embeds,\n",
    "            self_attn_scores=self_attn_scores,\n",
    "            cross_attn_scores=cross_attn_scores,\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zPrvXAXtN4yF"
   },
   "source": [
    "#### mask\n",
    "\n",
    "- mask实际上大类上只有两种\n",
    "    1. `padding_mask`：mask掉`pad_idx`，不计算损失\n",
    "    2. `attention_mask`：mask掉`pad_idx`，不计算注意力分数\n",
    "- Decoder的`attention_mask`和Encoder有一定的区别：\n",
    "    - Encoder可以同时看见序列所有信息，故只mask掉`pad_idx`\n",
    "    - Decoder只能看到在自身之前的序列的信息，故要额外mask掉自身之后的序列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "outputs": [
    {
     "data": {
      "text/plain": "tensor([[False,  True,  True,  True,  True],\n        [False, False,  True,  True,  True],\n        [False, False, False,  True,  True],\n        [False, False, False, False,  True],\n        [False, False, False, False, False]])"
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(torch.triu(torch.ones(5, 5)) == 0).transpose(-1,-2)"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "D2N9VmcAWLn1",
    "outputId": "560b8af9-c854-4fd7-df3e-86e9eb9045cc",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:25.527550400Z",
     "start_time": "2024-08-05T08:06:25.526551200Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 426
    },
    "id": "QxpSYOsaN4yF",
    "outputId": "1022d3c3-c72e-4798-b998-c71c6690b94e",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:26.193720900Z",
     "start_time": "2024-08-05T08:06:25.526551200Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 480x480 with 2 Axes>",
      "image/png": "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GVOmTNE4HL8mRt0ndvPmTYSEhKBfv37YtWsXWrRogbNnz8LZ2dnQoRERGS1DPTsxNjYWsbGxGt/LzMysUtajRw/8/PPPdb7Ow4w6iaWkpMDT0xNr1qxRlbVt29aAERERSYREnn2oK6NuTtyxYwcCAwPx4osvwsXFBd26dcOqVasMHRYRkVGrrInpukmBUSex8+fPY8WKFejQoQP27NmDN954A5MnT8a6deuqPaasrKzKDHMiIjJNRt2cqFQqERgYiHnz5gEAunXrhhMnTiA9PR1RUVEaj0lOTsYHH3zQkGESERkXLsViHNzd3dG5c2e1sscffxyXLl2q9piEhAQUFxertry8vEcdJhGRkZHpaTN+Rl0TCwkJwenTp9XKzpw5gzZt2lR7jFwu19tjUYiIyLgZdU3srbfews8//4x58+bh3Llz2LhxI1auXFnnyXBERGbFAJOdDcWok1j37t2xbds2fP755/D19cWHH36ItLQ0jB071tChEREZLzNKYkbdnAgAQ4YMwZAhQwwdBhERGaF61cTy8vJw+fJl1eusrCzExcVh5cqVeguMiIjqqXIpFl03CahXEhszZgz2798PAMjPz8czzzyDrKwsvPfee5g9e7ZeAyQiorpp6KfYG1K9mhNPnDiBoKAgAMAXX3wBX19f/Pjjj/juu+8wceJEzJw5U69BEumDvlaIBrhKNJGxqFcSq6ioUA1j37dvH5577jkAQKdOndSeUkxERAbAyc418/HxQXp6Og4ePIi9e/di4MCBAICrV6+iWbNmeg2QiIjqiH1iNUtJScEnn3yCvn37YvTo0fDz8wPw4IG9lc2MREREj1q9mhP79u2LoqIilJSUqK3t9dprr6Fx48Z6C46IiOpOJh5sup5DCupVE0tKSsLly5erLE7p5eUFFxcXvQRGRET1ZEaTneuVxL766it4e3ujf//+2LhxI8rKyvQdFxER1Rf7xGqWk5ODI0eOwMfHB1OmTIGbmxveeOMNHDlyRN/xERERVavez07s1q0bPvroI1y9ehWffvopLl++jJCQEHTt2hVLlixBcXGxPuMkIiJtsTlRe0IIVFRUoLy8HEIIODs7Y+nSpfD09MTmzZv1ESMREdUFk1jtjh07htjYWLi7u+Ott95Ct27dcPLkSfzwww84e/Ys5s6di8mTJ+szViIiIjX1SmJdunTBk08+idzc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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "def generate_square_subsequent_mask(sz: int) -> Tensor:\n",
    "    \"\"\"\n",
    "    Generate a square mask for the sequence. The masked positions are filled with True.\n",
    "        Unmasked positions are filled with False.\n",
    "    \"\"\"\n",
    "    # torch.ones(sz, sz): 创建一个全为 1 的 sz × sz 的矩阵。\n",
    "    # torch.triu(...): 使用 triu 函数取得矩阵的上三角部分，将主对角线以下部分置零。\n",
    "    mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "    # mask = torch.triu(torch.ones(sz, sz))\n",
    "    return mask\n",
    "\n",
    "\n",
    "plt.matshow(generate_square_subsequent_mask(16))\n",
    "plt.colorbar()\n",
    "plt.xlabel(\"keys\")\n",
    "plt.ylabel(\"querys\")\n",
    "plt.title(\"1 means mask while 0 means unmask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 960
    },
    "id": "cPeMjXO1N4yF",
    "outputId": "e6922ce8-ba71-491f-d7a6-709a14a4b7d7",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.178084Z",
     "start_time": "2024-08-05T08:06:25.538543600Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['[BOS]', '[UNK]', '[UNK]', 'brown', '[UNK]', 'jumps', 'over', 'the', '[UNK]', 'dog', '.', '[EOS]']\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 1000x500 with 2 Axes>",
      "image/png": "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rFn64x//qDFjxuiFF17QNddcIw8PD02ePNlpPzt27Kh9+/bpk08+0b/+9S/9/e9/15tvvqnnnntOM2fOdNr+b37zG7333ntKTk7WI488YmpfAKAu8/Hx0d133601a9boX//6l+69917T23j88ce1ePFiTZ48Wb/61a9ks9lksViUkJBQYf+50mt92XOWLl2q4ODgcut5ef3yv5au6H0VoY/BVQhSqJeuu+46PfbYY3rsscf0448/qnv37vrTn/6kV155RZJktVp11113uaSW06dPKy0tTTNnztRzzz3nGC97V+9Cl/sE97rw6e4ffvih7rjjDr399ttO42fOnCl3Ua2fn5/i4+MVHx+vwsJC3X///frTn/6kqVOnOp3yMnfuXHl5eemxxx5T06ZNL3kRLwC4yw033CBJ2rdvn9q0aeMYLyws1KFDhxz95sL17rzzTqdt7Nu3z/F4GYvFouTkZA0aNEhDhgzR2rVrFRMTY6q2Dz/8UCNHjtS8efMcY/n5+RXeTbAyym501Lx588v20cr2vqCgIPn6+lbYE/ft21elGq8GfQxmcGof6pWSkpJypyo0b95cISEhKigoUHh4uNq2bauXXnqpwlvHVnSL16tV9g7YxUeGXn311XLr+vn5SVKFDc7Pz6/Kjc9VPD09y+3n3/72t3K39b341reNGjXSzTffLMMwVFRU5PSYxWLRwoUL9dvf/lYjR47Uxx9/XDPFA0AV3XXXXWrUqJFee+01p9fAt99+W7m5ubrnnnskST169FDz5s21YMECFRQUONZbu3at9uzZ41jvQo0aNdKqVasUERGhgQMHatu2baZqq+h1+fXXX1dJSYmp7ZSJjY2V1WrVrFmzyr1eS7/00cr2Pk9PT8XGxuqjjz7S999/7xjfs2ePUlJSqlTj1aCPwQyOSKFeycvLU2hoqH7729/q1ltvlb+/v9atW6ft27dr3rx58vDw0KJFixQXF6dOnTpp9OjRatmypY4ePar169fLarXqH//4R7XWZLVaHbdgLSoqUsuWLZWamqpDhw6VWzc8PFySNG3aNCUkJMjb21sDBw6Un5+fwsPDtW7dOr388ssKCQlR69at1bNnz2qt9Wrde++9ev755zV69Gjdfvvt2r17t5KTk53eoZWk/v37Kzg4WL1791aLFi20Z88evfHGG7rnnnvUtGnTctv18PDQsmXLNHjwYA0dOlSffvppuXdzAcBdgoKCNHXqVM2cOVMDBgzQr3/9a+3bt09vvvmmIiIiNHz4cEmSt7e3Zs+erdGjRys6OloPPPCA4/bnrVq10hNPPFHh9n19ffXJJ5/ozjvvVFxcnDZs2KBbbrmlUrXde++9Wrp0qWw2m26++WZt2bJF69atU7Nmzaq0r1arVfPnz9dDDz2k7t27KyEhQUFBQfr+++/1z3/+U71799Ybb7xhqvfNnDlT//rXvxQZGanHHntMxcXFjs9o2rVrV5XqrCr6GExxz80CgZpRUFBgPPnkk8att95qNG3a1PDz8zNuvfVW480333Rab+fOncb9999vNGvWzPDx8TFuuOEGY+jQoUZaWppjneq8/Xl2drZx3333GQEBAYbNZjOGDBli5OTklLvtqmEYxgsvvGC0bNnS8PDwcJp/7969RlRUlOHr62tIctwK/cLbq16oovqv5FL7Fx0dXeFtXm+44QbjnnvucXyfn59vTJkyxbjuuusMX19fo3fv3saWLVuM6OhoIzo62rHeW2+9ZURFRTl+/m3btjWefPJJIzc317FORft1/vx5Izo62vD39ze2bt1a6f0CgOpW0WvsG2+8Ydx0002Gt7e30aJFC2P8+PHG6dOnyz13xYoVRrdu3QwfHx/jmmuuMR588EEjOzvbaZ2KXo9PnDhh3HzzzUZwcLCxf//+StV5+vRpY/To0ca1115r+Pv7G7GxscbevXuNG264wekjNcr2Z/v27U7PX79+vSHJWL9+fbnx2NhYw2azGY0bNzbatm1rjBo1ytixY4djHTO9b8OGDUZ4eLjRqFEjo02bNsaCBQscfcAM+hhcyWIYJq9EBwAAAIAGjmukAAAAAMAkrpECrkJubq5++umny65T0e1hXa2u1AkAqJyzZ89WeNOkCwUFBV3ylt91DX0MtRGn9gFXYdSoUXrvvfcuu05t+CdWV+oEAFTOjBkzyn1e0cUOHTqkVq1auaagGkYfQ21EkAKuwrfffqucnJzLruOqz6u6nLpSJwCgcg4ePKiDBw9edp0+ffo4fZ5RXUYfQ21EkAIAAAAAk7jZBAAAAACYRJACAAAAAJMIUnCp9PR0JSYmursMl8jMzNT8+fPdXYbbHD58WKmpqZKkHj16uLka1/j66681atQod5cBwCR6U8NBb0J1IkhVID09XWFhYVq4cKFiYmIUGRmpqKgoDRs2TCUlJZKk3bt3q2/fvoqOjta9996rrKwsSdKuXbsUFRWl6Oho3X777Tp69Ki+/fZbde3a9bIv0hfOefE/7LLvR40apbi4uHLjFzaALVu2qHfv3jpz5oxGjBih0NDQWjXn5ZSWllbpeZXhjn3t2rWrxo8fX9274lJX8zu5sFnVhNr291sfVfRaGBMTo5iYGOXm5qq0tFTPPvusIiMj1adPH7322muO5yYmJqp3797q06ePXnjhBUnSU089pYCAgCveshnluaMvXTwvvan60Zuqht7UsNWm3kSQuoT4+HiNGzdOkrR27VplZGTI399fW7ZsUVFRkYYPH66FCxdqw4YNmjp1qoYPHy5JeuGFFzR//nxt2LBBaWlpatasmW6++Wa9+uqrpua8lOzsbO3atavCx3bv3q1JkyZp1apVCggI0JIlS674mQrumHPXrl0aOHCgIiIitHv3bnXv3l2TJk3SQw89pOzsbN11112KiorSxIkTJUkPPvigcnJylJaWprZt20qSZs6cqfXr12vGjBl66KGHdPfddys6OvqynzHh6n0te0G88IW07OsZM2Zo+PDhiouLU1xcnObPn6+YmBjFx8dLkt59910NHjxYd999tyIjI3X06FGdOnVKMTExuuOOOzRo0KDL7ocklZSUaPjw4YqOjtY999yjuXPnasWKFZKk7777Tg888IAkadasWYqOjlZUVJR2794tSU6/k6qaP3++VqxYoZiYGJ07d04jR45U165dlZycLOnnO07FxsYqJiZGTzzxRJXmcMff78WKi4s1dOhQ3XXXXXrllVckScuXL1fPnj3Vq1cvpaSkSJJSU1PVrVs3DRkyRFFRUTp8+LCpedzl4tfC9PR0paeny2az6e2339apU6e0ceNGpaenKyUlRevWrdM333yjI0eOaPPmzdq0aZPj3/KcOXPUtWtXN+5N3eaOvnTxvJdCb6I30Zt+QW+qebWlNxGkTMjLy5PVatXWrVvVtWtXxwtn7969VVpaqqysLPn6+mrdunU6d+6cfH19q/22o4mJiZozZ0658UOHDmnMmDFauXKlWrRoUavnPH/+vD7++GMtWbJE06ZN0+nTp/X4448rOTlZf/7zn5WYmKiMjAz99NNPysjIUJ8+fbRx40Zt3LhRHTp00NGjR/Xll1+qV69ekqT27dvr008/Va9evfTZZ5/Vqn29nI4dO2rt2rUKDAxUYWGh0tPTVVhY6LidbZMmTfTpp59q2rRpmj17tnbu3KnbbrtN69ev1+rVq6+4/dWrVys0NFQbNmxQQkKCzp8/r5UrV0qSVqxYofj4eH399dfat2+fNmzYoOXLl+vZZ5+VJKffSVWNHz9e8fHxSk9P17Fjx/T6668rIyPD8c7QM888ozfffFPp6enKz8/Xjh07qjzX5dT07/Sjjz5Su3bttG7dOkVERKikpERJSUnasGGDUlNTNW3aNEnSc889p7S0NC1btsxxpKCuW758uZ588klJkpeXl/7whz/ogw8+UOPGjbV//37t2bNHkhQYGOjOMuu12tCXJHoTvYneZBa9qea4sjcRpCohLi5O3bp1U3Z2tjp27KicnByFhIQ4rRMaGqqcnBzNnTtXe/bs0a233qr4+HidO3euWmsJDw/XiRMndOTIEafxtLQ09ezZs0Y+eK+65+zWrZssFos6duyo//3vfwoMDFS7du0kSQcOHFBERIQkKSIiQvv371dkZKQyMjK0b98+jR07VmlpaSouLpavr69je5IUFham06dP16p9vdiFnzbQpUsXSVJISIjj65YtWzr2ITw8XNIvP4fo6Gj5+fnpwQcf1Msvv3zFuS7+WR44cEC5ubmy2+1KSUlRXFycvv32W33xxReKiYnRsGHDHIe1L/ydVIc2bdrIarXKarU6TkPau3evxo4dq5iYGG3btk3Z2dnVNt+Favp3euDAAaff1fHjx3X99dercePGslqt8vb2VnFxsUpKSnTNNdfIx8dHt9xyy1XN6S5xcXGKiYlxnJJy8Wth2etg27Zt9cwzz+ixxx7TjTfeqDVr1rir5HqrNvUlid5Eb6I3mUVvqj7u7E0EqUpYu3atdu7cqSFDhmjevHm67rrryn0oXHZ2tkJCQtSiRQstWLBABw4cUPv27bV06VLT81ksFsfX+fn5jhflMlOmTNG8efOcxsaMGaOjR4/qnXfeMT2fq+fMzMyUYRjat2+frrvuOnl4/PJn2K5dO23btk2StH37drVv316dOnVSZmamGjVqpKioKL322mvq3r17hbVX5mPRXP3z9fT0VF5envLy8pw+PPHCOirah507d0qSduzYoXbt2qmoqEjTp09XcnKyUlNT9f3331923op+loMHD9bs2bPVpk0b+fj46KabblJ0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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n",
      "['[BOS]', '[UNK]', 'does', 'the', '[UNK]', 'say', '?', '[EOS]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 1000x500 with 2 Axes>",
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1AAAAG1CAYAAAD3DRUpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABHu0lEQVR4nO3de1xUdf7H8TeIAoIzWOGFIFMzzTItvP1UZMTk0mUp826rqJub2oalbvro4q0kI9MuW8q6aRc3M6XazUi84SUtbVPRTV3LSyBamgreQC7n98c+mGUC8YA4M8Dr+XjMY5kzZ76fz2GFdx/OzBkPwzAMAQAAAACuyNPVDQAAAABAdcEABQAAAAAmMUABAAAAgEkMUAAAAABgEgMUAAAAAJjEAAUAAAAAJjFAAQAAAIBJDFAAAAAAYBIDFAAAAACYxAAFlGHx4sXy8PDQ4cOHHbYnJiaqRYsWqlOnjjp06OCS3mqDadOmycPDQydPnnR1KwBQyuUyoqrExcXJ39//mqxdluLj+fbbb51W80qKc6C6IsdqNgYowKTU1FT9+c9/Vvfu3bVo0SLNmjWrStffsmWLpk2bpjNnzpR6bNasWfr000+rtB4AAAAqjgEKMGndunXy9PTU3/72Nw0bNkz33ntvla6/ZcsWTZ8+nQEKAADAjTFAASb98ssv8vX1Vb169VzdCgAAAFyEAQo1ytmzZzV+/HjdfPPN8vb2VqNGjdSnTx9999139n2++eYbRUdHy2q1qn79+goPD9dXX31V7roeHh5atGiRzp8/Lw8PD3l4eGjx4sWmekpPT1dcXJxatGghHx8fNWnSRCNHjtSvv/5q32fatGmaNGmSJKl58+b2GocPH5aHh4fOnz+vd9991749Li7O/jwPDw/98MMPiouLU0BAgKxWq0aMGKELFy5U6HtX/Jr7n376Sffff7/8/f1144036i9/+Yskaffu3YqIiJCfn5+aNWumv//97w7PP3XqlCZOnKh27drJ399fFotFMTEx2rVrV6lab7zxhm6//XbVr19fDRs2VMeOHUut91tHjhzRLbfcojvuuEM///xzhY4NAK61t956S7fffru8vb0VFBSkcePGlfmKgo8//lihoaHy9fXVDTfcoEceeURHjx694vo7d+5UYGCgbDabzp07Z6qnI0eOaOzYsWrdurV8fX11/fXXq3///pd971ZeXp6eeuopBQYGys/PTw899JBOnDhRar+UlBSFhYXJz89PDRo00H333ad///vfDvuYyb5imzdvVqdOneTj46OWLVtqwYIFpo7vt8gxOIuXqxsAqtJjjz2m5cuX6/HHH1fbtm3166+/avPmzdq7d6/uvvturVu3TjExMQoNDdXUqVPl6empRYsWKSIiQps2bVLnzp3LXPf9999XUlKStm3bpoULF0qSunXrZqqn1atX6+DBgxoxYoSaNGmif//730pKStK///1vff311/Lw8FDfvn31n//8Rx9++KHmzp2rG264QZIUGBio999/X3/4wx/UuXNnjR49WpLUsmVLhxoDBgxQ8+bNlZCQoO+++04LFy5Uo0aNNHv27Ap9/woLCxUTE6OePXvq5Zdf1pIlS/T444/Lz89PzzzzjIYOHaq+fftq/vz5GjZsmP7v//5PzZs3lyQdPHhQn376qfr376/mzZvr559/1oIFCxQeHq7vv/9eQUFBkqS//vWveuKJJ9SvXz/Fx8crNzdX6enp+uabbzRkyJAy+/rxxx8VERGh6667TqtXr7Z/fwDAHUybNk3Tp0/XPffcozFjxmj//v16++23tX37dn311VeqW7eupP9erGHEiBHq1KmTEhIS9PPPP+u1117TV199pR07diggIKDM9bdv366oqCh17NhRn332mXx9fU31tX37dm3ZskWDBg1ScHCwDh8+rLfffls2m03ff/+96tev77D/n/70JzVs2FBTp07V4cOHNW/ePD3++OP66KOP7Pu8//77Gj58uKKiojR79mxduHBBb7/9tnr06KEdO3bo5ptvlmQu+6T/DjWRkZEKDAzUtGnTVFBQoKlTp6px48YV/H/hv8gxOIUB1CBWq9UYN25cmY8VFRUZrVq1MqKiooyioiL79gsXLhjNmzc3+vTpY9+2aNEiQ5Jx6NAh+7bhw4cbfn5+Fe7pwoULpbZ9+OGHhiRj48aN9m2JiYmlahbz8/Mzhg8fXmr71KlTDUnGyJEjHbY/9NBDxvXXX1+hPocPH25IMmbNmmXfdvr0acPX19fw8PAwli5dat++b98+Q5IxdepU+7bc3FyjsLDQYc1Dhw4Z3t7exowZM+zbYmNjjdtvv73cXoqP68SJE8bevXuNoKAgo1OnTsapU6cqdEwAcC2UzIhffvnFqFevnhEZGenwO/DNN980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9cEHH+jBBx/UgAED9MUXX5T66y0AuEpgYKCmTJmi6dOnKzo6Wr/73e+0f/9+vfXWW+rUqZMeeeQRSVLdunU1e/ZsjRgxQuHh4Ro8eLD9MuY333yznnzyyTLX9/X11eeff66IiAjFxMRow4YNuuOOO0z1dv/99+v999+X1WpV27ZttXXrVq1Zs0bXX399pY7VYrHo7bff1u9//3vdfffdGjRokAIDA/XTTz9p5cqV6t69u958880KZd/06dP15ZdfKiwsTGPHjlVBQYH9M5bS09Mr1WdlkWMwzTUX/wOqXl5enjFp0iSjffv2RoMGDQw/Pz+jffv2xltvveWw344dO4y+ffsa119/veHt7W00a9bMGDBggLF27Vr7PlV5GfPMzEzjoYceMgICAgyr1Wr079/fyMrKKnX5VMMwjJkzZxo33nij4enp6VB/3759Rs+ePQ1fX19Dkv2S5iUvk1pSWf1fyeWOLzw8vMzLtTZr1sy477777Pdzc3ONCRMmGE2bNjV8fX2N7t27G1u3bjXCw8ON8PBw+34LFiwwevbsaf/+t2zZ0pg0aZKRnZ1t36es47pw4YIRHh5u+Pv7G19//bXp4wKAqlbW79g333zTaNOmjVG3bl2jcePGxpgxY4zTp0+Xeu5HH31k3HXXXYa3t7dx3XXXGUOHDjUyMzMd9inr9/HJkyeNtm3bGk2aNDEOHDhgqs/Tp08bI0aMMG644QbD39/fiIqKMvbt22c0a9bM4aMxio9n+/btDs9fv369IclYv359qe1RUVGG1Wo1fHx8jJYtWxpxcXHGt99+a9+nItm3YcMGIzQ01KhXr57RokULY/78+fYcqAhyDM7iYRgVfJc5AAAAANRSvAcKAAAAAEziPVBAJWVnZ+vixYvl7lPWZV6drbr0CQAw59y5c2VeDKmkwMDAy166u7ohx+BueAkfUElxcXF69913y93HHX68qkufAABzpk2bVurzhn7r0KFDuvnmm53T0DVGjsHdMEABlfT9998rKyur3H2c9XlT5akufQIAzDl48KAOHjxY7j49evRw+Dyi6owcg7thgAIAAAAAk7iIBAAAAACYxAAFAAAAACYxQMFuz549iouLc2rNw4cPKzU1VZLUsWNHp9aurb755ht1795dd999tz744ANXtwMAl+WKXJLIJlcgm1CdMECVkJaWppCQECUlJclmsyksLEw9e/bUkCFDVFhYKEnavXu3evfurfDwcN1///3KyMiQJKWnp6tnz54KDw9Xt27ddPToUX3//ffq0KGDJk6caKrmb39JF9+Pi4tTTExMqe1paWn2tbdu3aru3bvrzJkzGjZsmIKDg6vuG3MNlQypa6G2f3/L0rRpU61bt05btmzRa6+9dtXrlfVzY7PZZLPZlJ2draKiIj377LMKCwtTjx499Prrr9ufO3HiRHXv3l09evTQzJkzJUl//vOfFRAQUO4lesuq2blzZ/saktStWzfNmDHDfn/x4sVq1aqVIiIiFBYWpgULFtgfi4yMNPUfSa6oW1tq4vLIJucjm5yPbCKb3LHmZRmwW79+vTFhwgTDMAwjPDzcOHv2rGEYhvHoo48amzZtMi5dumTceeedxg8//GAYhmFs3rzZ6Nmzp2EYhtGvXz9jz549hmEYxoULF4yLFy+WWvNKNUNDQx0eK74/fPhw44477jB27drlsL34uenp6UanTp2M48ePl3ruleTn5xv9+/c3evfubYwcOdIYPny48eGHHxqdO3c2unTpYnz55ZeGYRjG9u3bDZvNZvTo0cNITEw0DMMw3n77baNTp05Gr169jOTkZFP1fmvAgAFGcHCwER4ebrRp08YYNmyY0b59e+ODDz4wDMMwfvzxRyMyMtIIDw83xo8fX+H1Xf39LWnr1q1G586dDZvNZkydOtV48sknjZ49exqdOnUyduzYYfzyyy/Gvffea98/IiLCyM7OrnAdszZs2GAMHTr0qte53M9NsaSkJGPMmDGGYfz339u9995rrF692tizZ4/Rr18/+36nTp2yf13WOleqmZ+fb9x5551GRkaG8dNPPxn9+/c3evXqZX/OokWLjDfeeMMwDMM4f/68ERkZaXz++ef2x838f+qKurWlJi6vtmWTq3PJMGpPNrlbLhkG2UQ2uVfNy+EMlAlnz56VxWLR119/rQ4dOqhly5aSpO7du6uoqEgZGRny9fXVmjVrdP78efn6+lb5pUMnTpyol19+udT2Q4cOaeTIkVq2bJkaN25c4XU//fRT3XLLLVqzZo06deqkwsJCJSQkaMOGDUpNTdUzzzwjSZo8ebKSk5O1adMmbdiwQT///LOWLVumNWvWaN26dYqNja3UcY0ZM0YDBw5UWlqajh8/rjfeeEMbN260/yVo8uTJeuutt5SW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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "#通过下面代码查看mask的效果\n",
    "inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "inputs_ids, input_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "for i in range(len(inputs_words)):\n",
    "    decode_text = tokenizer.decode(inputs_ids[i: i+1].tolist(), remove_bos=False, remove_eos=False, remove_pad=False, split=True)[0]\n",
    "    print(decode_text)\n",
    "    self_attn_mask  = input_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0)\n",
    "    look_ahead_mask = generate_square_subsequent_mask(inputs_ids.shape[-1])\n",
    "\n",
    "    fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axs[0].matshow(self_attn_mask)\n",
    "    axs[0].set_title(\"self_attn_mask\")\n",
    "    axs[0].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_ylabel(\"querys\")\n",
    "    axs[0].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_xlabel(\"keys\")\n",
    "    axs[1].matshow(look_ahead_mask)\n",
    "    axs[1].set_title(\"look_ahead_mask\")\n",
    "    axs[1].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_ylabel(\"querys\")\n",
    "    axs[1].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_xlabel(\"keys\")\n",
    "    plt.show()\n",
    "    print('-'*50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "outputs": [
    {
     "data": {
      "text/plain": "tensor([[False,  True,  True,  True,  True],\n        [False, False,  True,  True,  True],\n        [False, False, False,  True,  True],\n        [False, False, False, False,  True],\n        [False, False, False, False, False]])"
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(torch.triu(torch.ones(5, 5)) == 0).transpose(-1, -2).bool()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.179078300Z",
     "start_time": "2024-08-05T08:06:26.513547400Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "outputs": [
    {
     "data": {
      "text/plain": "torch.Size([5, 1, 4, 4])"
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#帮我随机两个[5, 1, 1, 4]与[1, 1, 4, 4]尺寸的张量，并求和\n",
    "a = torch.randn(5, 1, 1, 4)\n",
    "b = torch.randn(1, 1, 4, 4)\n",
    "(a + b).shape"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.234057200Z",
     "start_time": "2024-08-05T08:06:27.159090100Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TelCDOoEN4yF"
   },
   "source": [
    "#### Transformer Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "id": "oFGNt8FPN4yF",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.234057200Z",
     "start_time": "2024-08-05T08:06:27.160089800Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerOutput:\n",
    "    logits: Tensor\n",
    "    encoder_last_hidden_states: Tensor\n",
    "    encoder_attn_scores: List[Tensor] #画图\n",
    "    decoder_last_hidden_states: Tensor\n",
    "    decoder_self_attn_scores: List[Tensor] #画图\n",
    "    decoder_cross_attn_scores: List[Tensor] #画图\n",
    "    preds: Optional[Tensor] = None\n",
    "\n",
    "class TransformerModel(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_encoder_layers = config[\"num_encoder_layers\"]\n",
    "        self.num_decoder_layers = config[\"num_decoder_layers\"]\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        self.bos_idx = config[\"bos_idx\"]\n",
    "        self.eos_idx = config[\"eos_idx\"]\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "        self.share = config[\"share_embedding\"]\n",
    "\n",
    "        # layers\n",
    "        self.src_embedding = TransformerEmbedding(config) # 输入的嵌入层\n",
    "        if self.share:#如果共享词嵌入，则使用src_embedding作为trg_embedding\n",
    "            self.trg_embedding = self.src_embedding #源和目标的嵌入层相同，共享参数，节省内存\n",
    "            self.linear = lambda x: torch.matmul(\n",
    "                x, self.trg_embedding.get_word_embedding_weights().T\n",
    "            ) # 输出层，共享参数，直接拿原有embedding矩阵的转置，节省内存\n",
    "        else:\n",
    "            self.trg_embedding = TransformerEmbedding(config) #decoder模块的嵌入层\n",
    "            self.linear = nn.Linear(self.hidden_size, self.vocab_size) # 输出层\n",
    "\n",
    "        self.encoder = TransformerEncoder(config)\n",
    "        self.decoder = TransformerDecoder(config)\n",
    "\n",
    "        # init weights\n",
    "        self._init_weights()\n",
    "\n",
    "    def _init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化权重\"\"\"\n",
    "        for p in self.parameters():\n",
    "            if p.dim() > 1:\n",
    "                nn.init.xavier_uniform_(p)\n",
    "\n",
    "    def generate_square_subsequent_mask(self, sz: int) -> Tensor:\n",
    "        \"\"\"\n",
    "        Generate a square mask for the sequence. The masked positions are filled with True.\n",
    "            Unmasked positions are filled with False.为了生成斜三角的mask\n",
    "        \"\"\"\n",
    "        mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "\n",
    "        return mask\n",
    "\n",
    "    def forward(\n",
    "        self, encoder_inputs, decoder_inputs, encoder_inputs_mask=None\n",
    "    ) -> TransformerOutput:\n",
    "        # encoder_inputs: [batch_size, src_len]\n",
    "        # decoder_inputs: [batch_size, trg_len]\n",
    "        # encoder_inputs_mask: [batch_size, src_len]\n",
    "        if encoder_inputs_mask is None:\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx) # [batch_size, src_len]\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(\n",
    "            2\n",
    "        )  # [batch_size, 1, 1, src_len],用于encoder的自注意力\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(decoder_inputs.shape[1])\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(decoder_inputs.device)\n",
    "        )  #[trg_len, trg_len]--> [1, 1, trg_len, trg_len],用于decoder的自注意力\n",
    "        #增加decoder_inputs_mask和look_ahead_mask进行组合\n",
    "        decoder_inputs_mask = decoder_inputs.eq(self.pad_idx) # [batch_size, trg_len]，和上面encoder_inputs_mask一致\n",
    "        # print(decoder_inputs_mask.shape)\n",
    "        decoder_inputs_mask = decoder_inputs_mask.unsqueeze(1).unsqueeze(2)  # [batch_size, 1, 1, trg_len]\n",
    "        # print(decoder_inputs_mask.shape)\n",
    "        decoder_inputs_mask = decoder_inputs_mask + look_ahead_mask # [batch_size, 1, 1, trg_len]与[1, 1, trg_len, trg_len]相加，得到decoder的自注意力mask\n",
    "\n",
    "        # encoding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds, encoder_inputs_mask) #encoder_inputs_mask用于encoder的自注意力,广播去做计算\n",
    "\n",
    "        # decoding\n",
    "        decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "        decoder_outputs = self.decoder(\n",
    "            decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "            encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "            attn_mask=decoder_inputs_mask, #用于decoder的自注意力,广播去做计算\n",
    "            cross_attn_mask=encoder_inputs_mask,#用于decoder的交叉注意力,广播去做计算\n",
    "        )\n",
    "\n",
    "        logits = self.linear(decoder_outputs.last_hidden_states) # [batch_size, trg_len, vocab_size]\n",
    "\n",
    "        return TransformerOutput(\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )\n",
    "\n",
    "    @torch.no_grad()\n",
    "    def infer(self, encoder_inputs, encoder_inputs_mask=None) -> Tensor:\n",
    "        # assert len(encoder_inputs.shape) == 2 and encoder_inputs.shape[0] == 1\n",
    "        if encoder_inputs_mask is None:#应对多个样本同时进行推理\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx)\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(2)  # [batch_size, 1, 1, src_len],[1,src_len]相加时，会自动广播到[batch_size,1,src_len,src_len]\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(self.max_length)\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(encoder_inputs.device)\n",
    "        )  # [1, 1, trg_len, trg_len]\n",
    "\n",
    "        # encoding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds) #因为只支持单样本预测，没有paddings，所以不需要mask\n",
    "\n",
    "        # decoding,多样本推理\n",
    "        decoder_inputs = torch.Tensor([self.bos_idx] * encoder_inputs.shape[0]).reshape(-1, 1).long().to(device=encoder_inputs.device)\n",
    "        for cur_len in tqdm(range(1, self.max_length + 1)):\n",
    "            decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "            decoder_outputs = self.decoder(\n",
    "                decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "                encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "                attn_mask=look_ahead_mask[:, :, :cur_len, :cur_len],#decoder的自注意力mask\n",
    "            )\n",
    "\n",
    "            logits = self.linear(decoder_outputs.last_hidden_states)\n",
    "            next_token = logits.argmax(dim=-1)[:, -1:] #通过最大下标确定类别，[:, -1:]表示取最后一个结果\n",
    "            decoder_inputs = torch.cat([decoder_inputs, next_token], dim=-1) #预测输出拼接到输入中\n",
    "            #(decoder_inputs == self.eos_idx).sum(dim=-1)是判断样本中是否含有EOS标记\n",
    "            #all是每一个都为True，才会结束\n",
    "            if all((decoder_inputs == self.eos_idx).sum(dim=-1) > 0):\n",
    "                break\n",
    "\n",
    "        return TransformerOutput(\n",
    "            preds=decoder_inputs[:, 1:],\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kXHQ3sGTN4yG"
   },
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OxNA8DIzN4yG"
   },
   "source": [
    "### 损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "id": "1xzahHANN4yG",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.234057200Z",
     "start_time": "2024-08-05T08:06:27.160089800Z"
    }
   },
   "outputs": [],
   "source": [
    "class CrossEntropyWithPadding:\n",
    "    def __init__(self, config):\n",
    "        self.label_smoothing = config[\"label_smoothing\"]\n",
    "\n",
    "    def __call__(self, logits, labels, padding_mask=None):\n",
    "        # logits.shape = [batch size, sequence length, num of classes]\n",
    "        # labels.shape = [batch size, sequence length]\n",
    "        # padding_mask.shape = [batch size, sequence length]\n",
    "        bs, seq_len, nc = logits.shape\n",
    "        loss = F.cross_entropy(logits.reshape(bs * seq_len, nc), labels.reshape(-1), reduce=False, label_smoothing=self.label_smoothing) #label_smoothing表示随机将一个类别的概率设置为0.1，使得模型更加关注其他类别\n",
    "        if padding_mask is None:\n",
    "            loss = loss.mean()\n",
    "        else:\n",
    "            padding_mask = 1 - padding_mask.reshape(-1) #将padding_mask reshape成一维张量，mask部分为0，非mask部分为1\n",
    "            loss = torch.mul(loss, padding_mask).sum() / padding_mask.sum()\n",
    "\n",
    "        return loss\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zuBC9KqFN4yG"
   },
   "source": [
    "### 学习率衰减"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "outputs": [
    {
     "data": {
      "text/plain": "[<matplotlib.lines.Line2D at 0x210b5030380>]"
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 1 Axes>",
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAGdCAYAAAAxCSikAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABP50lEQVR4nO3deXxU5aHG8d9kZ0nCEkhYAgFZwhKysITggi2pUamKawhcodSr1bIaRYEq1NZraAWLECq1vdXeW1lEBSkiFqMgShTJAoR9D1sSwpKEhGwz7/2Da9pIgExMmMnk+X4+85Gcec/keT0J52HeMzMWY4xBRERExIm5OTqAiIiIyPWosIiIiIjTU2ERERERp6fCIiIiIk5PhUVEREScngqLiIiIOD0VFhEREXF6KiwiIiLi9DwcHaC+2Gw2Tp06ha+vLxaLxdFxREREpBaMMRQVFdGxY0fc3K7+PIrLFJZTp04RHBzs6BgiIiJSB8ePH6dz585Xvd9lCouvry9wecJ+fn4OTiMiIiK1UVhYSHBwcNV5/GpcprB8twzk5+enwiIiItLIXO9yDl10KyIiIk5PhUVEREScngqLiIiIOD0VFhEREXF6KiwiIiLi9OpUWBYvXkxISAg+Pj5ER0ezdevWq47dtWsXDz74ICEhIVgsFhYsWFDjuJMnT/If//EftG3blmbNmhEWFsa2bdvqEk9ERERcjN2FZcWKFSQmJjJnzhzS09MJDw8nLi6OvLy8GseXlJTQvXt35s6dS1BQUI1jzp8/z80334ynpycff/wxu3fvZv78+bRu3dreeCIiIuKCLMYYY88O0dHRDB48mOTkZODyW+IHBwczefJkZsyYcc19Q0JCmDZtGtOmTau2fcaMGXz11Vds3rzZvvT/prCwEH9/fwoKCvQ+LCIiIo1Ebc/fdj3DUl5eTlpaGrGxsf96ADc3YmNjSU1NrXPYNWvWMGjQIB5++GHat29PZGQkf/7zn6+5T1lZGYWFhdVuIiIi4prsKiz5+flYrVYCAwOrbQ8MDCQnJ6fOIQ4fPswbb7xBz549+eSTT3jqqaeYMmUKf/vb3666T1JSEv7+/lU3fY6QiIiI63KKVwnZbDaioqJ45ZVXiIyM5IknnuDxxx9nyZIlV91n5syZFBQUVN2OHz9+AxOLiIjIjWRXYQkICMDd3Z3c3Nxq23Nzc696QW1tdOjQgb59+1bb1qdPH7Kzs6+6j7e3d9XnBunzg0RERFybXYXFy8uLgQMHkpKSUrXNZrORkpJCTExMnUPcfPPN7Nu3r9q2/fv307Vr1zo/poiIiPxwxhj+N/Uos1btdGgOuz+tOTExkfHjxzNo0CCGDBnCggULKC4uZsKECQCMGzeOTp06kZSUBFy+UHf37t1Vfz558iSZmZm0bNmSHj16APD0008zbNgwXnnlFR555BG2bt3Km2++yZtvvllf8xQRERE7FZZWMOP9Hazbefk61bv6B3Frz3YOyWL3y5oBkpOTefXVV8nJySEiIoKFCxcSHR0NwO23305ISAhvv/02AEePHqVbt25XPMbw4cPZuHFj1ddr165l5syZHDhwgG7dupGYmMjjjz9e60x6WbOIiEj92XHiAhOXpnP83CU83S08f2coj93SDYvFUq/fp7bn7zoVFmekwiIiIvLDGWN466ujJH28hwqroXPrZiSPiSIiuFWDfL/anr/tXhISERER13ShpJzp7+1gw+7LL665s18Qv3toAP7NPB2cTIVFREREgPTs80xemsHJC5fwcnfjVyP7MC6ma70vAdWVCouIiEgTZrMZ/vLlYX6/fh+VNkPXts1ZPCaK/p38HR2tGhUWERGRJupccTnPrtzOZ3svf4DxTwd0IOmBMHx9HL8E9H0qLCIiIk3Qt0fPMWVZBqcLSvHycGPOPX0ZM6SL0ywBfZ8Ki4iISBNisxne2HSI1zbsx2ozdA9oQfKYKPp2dO5X2KqwiIiINBH5F8t4ekUmmw/kA3B/ZCdeHtWfFt7OXwecP6GIiIj8YKmHzjJ1eQZ5RWX4eLrxm3v78/Cgzk67BPR9KiwiIiIuzGozJH92kNdT9mMz0LN9SxaPjaJXoK+jo9lFhUVERMRF5RWVMm15JlsOnQXg4YGdeem+fjT3anyn/8aXWERERK7rywP5TFuRSf7FMpp7ufPyqP48ENXZ0bHqTIVFRETEhVRabbyecoDkzw9iDIQG+ZI8Jooe7Vs6OtoPosIiIiLiInIKSpmyPIOtR84BkDCkC3Pu6YuPp7uDk/1wKiwiIiIuYOO+PBLf3c654nJaeLmT9OAA7g3v6OhY9UaFRUREpBGrsNqY/8/9LNl0CIC+HfxYPDaKbgEtHJysfqmwiIiINFKnLlxi8rIM0o6dB2BcTFdm3d3HJZaAvk+FRUREpBH6dHcuz763nQslFfh6e/C7hwZwd1gHR8dqMCosIiIijUh5pY3fr9/LX748AsCAzv4kJ0TRpW1zBydrWCosIiIijcTxcyVMWpbB9uMXAPj5zd2YcVcoXh5ujg12A6iwiIiINALrs3J47r3tFJZW4ufjwbyHw7mjX5CjY90wKiwiIiJOrKzSStK6vby95SgAkV1asSghks6tXXsJ6PtUWERERJzUsbPFTFqawc6TBQD84rbuPBvXG093118C+j4VFhERESe0dscpZry/k4tllbRu7sn8R8L5cWigo2M5jAqLiIiIEymtsPLbtbt555tsAAaHtGZhQiQd/Js5OJljqbCIiIg4icNnLjJxaQZ7ThcC8MvbbyLxJ73waIJLQN+nwiIiIuIEVmecZNaqnZSUW2nbwovX4iMY3qudo2M5DRUWERERB7pUbuXXa3axYttxAIZ2b8ProyMJ9PNxcDLnosIiIiLiIAfzipj4Tgb7couwWGDKj3syZURP3N0sjo7mdFRYREREHOC9tBO8uDqLSxVW2vl683p8BMN6BDg6ltNSYREREbmBissqefHDLD5IPwnALT0C+EN8BO18vR2czLmpsIiIiNwge3MKmfhOOofOFONmgcSf9OKp23toCagWVFhEREQamDGGFd8eZ86aXZRV2gj082bh6Eiiu7d1dLRGo04v7F68eDEhISH4+PgQHR3N1q1brzp2165dPPjgg4SEhGCxWFiwYME1H3vu3LlYLBamTZtWl2giIiJO5WJZJVOXZzLjg52UVdoY3qsd66bcqrJiJ7sLy4oVK0hMTGTOnDmkp6cTHh5OXFwceXl5NY4vKSmhe/fuzJ07l6Cga3+q5Lfffsuf/vQnBgwYYG8sERERp7PrVAH3LPqSNdtP4e5m4fk7Q3nrZ4Np21LXq9jL7sLy2muv8fjjjzNhwgT69u3LkiVLaN68OX/9619rHD948GBeffVVRo8ejbf31Q/QxYsXGTt2LH/+859p3bq1vbFERESchjGG//36GPf/cQtH8ovp6O/Du78YylO334SbrlepE7sKS3l5OWlpacTGxv7rAdzciI2NJTU19QcFmThxIiNHjqz22NdSVlZGYWFhtZuIiIijFZZWMGlpBi+uzqK80kZsn/Z8NOVWBnZt4+hojZpdF93m5+djtVoJDKz+aZGBgYHs3bu3ziGWL19O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QIDJy8szxlx+6VqXLl3MZ599ZrZt22ZiYmJMTExM1f7fvXTtjjvuMJmZmWb9+vWmXbt2Nb50bfr06WbPnj1m8eLFDfay5qKiIpORkWEyMjIMYF577TWTkZFhjh07Zoy5/LLmVq1amQ8//NDs2LHD3HfffTW+rDkyMtJ888035ssvvzQ9e/as9pLfCxcumMDAQPPoo4+arKwss3z5ctO8efMrXvLr4eFh5s2bZ/bs2WPmzJlTby9rvtYci4qKzLPPPmtSU1PNkSNHzKeffmqioqJMz549TWlpaaOY41NPPWX8/f3Nxo0bq70stKSkpGrMjfq5bIjf4+vN7+DBg+Y3v/mN2bZtmzly5Ij58MMPTffu3c1tt93WKOZnjDEzZswwmzZtMkeOHDE7duwwM2bMMBaLxfzzn/80xjTu43e9+bnC8avJ91/51NiOoQrLdSxatMh06dLFeHl5mSFDhpivv/7a0ZGuEB8fbzp06GC8vLxMp06dTHx8vDl48GDV/ZcuXTK//OUvTevWrU3z5s3N/fffb06fPl3tMY4ePWruuusu06xZMxMQEGCeeeYZU1FRUW3M559/biIiIoyXl5fp3r27eeuttxpkPp9//rkBrriNHz/eGHP5pc0vvviiCQwMNN7e3mbEiBFm37591R7j7NmzJiEhwbRs2dL4+fmZCRMmmKKiompjtm/fbm655Rbj7e1tOnXqZObOnXtFlnfffdf06tXLeHl5mX79+pmPPvqowedYUlJi7rjjDtOuXTvj6elpunbtah5//PErfrmdeY41zQ2o9jNzI38u6/v3+Hrzy87ONrfddptp06aN8fb2Nj169DDTp0+v9j4ezjw/Y4z5+c9/brp27Wq8vLxMu3btzIgRI6rKijGN+/hdb36ucPxq8v3C0tiOocUYY+x7TkZERETkxtI1LCIiIuL0VFhERETE6amwiIiIiNNTYRERERGnp8IiIiIiTk+FRURERJyeCouIiIg4PRUWERERcXoqLCIiIuL0VFhERETE6amwiIiIiNNTYRERERGn939a+EZEJEJHuwAAAABJRU5ErkJggg=="
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x=np.arange(1, 40000)\n",
    "plt.plot(x, x * (4000 ** (-1.5)))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.235056800Z",
     "start_time": "2024-08-05T08:06:27.160089800Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "outputs": [
    {
     "data": {
      "text/plain": "22.627416997969522"
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sqrt(512)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.235056800Z",
     "start_time": "2024-08-05T08:06:27.161089200Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 449
    },
    "id": "UQPiKK4nN4yG",
    "outputId": "63cfb132-ef75-4f94-f0c8-86a2f36e09c2",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.235056800Z",
     "start_time": "2024-08-05T08:06:27.161089200Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 1 Axes>",
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# NoamDecayScheduler 是一个自定义或外部定义的学习率衰减调度器类。它需要接收配置 config 作为参数，可能实现了特定的学习率衰减方案\n",
    "class NoamDecayScheduler:\n",
    "    def __init__(self, config):\n",
    "        self.d_model = config[\"d_model\"]\n",
    "        self.warmup_steps = config[\"warmup_steps\"]\n",
    "\n",
    "    def __call__(self, step):\n",
    "        step += 1\n",
    "        arg1 = step ** (-0.5) #4000步之后是arg1\n",
    "        arg2 = step * (self.warmup_steps ** (-1.5))  #4000步之前是arg2\n",
    "\n",
    "        arg3 = self.d_model ** (-0.5)\n",
    "\n",
    "        return arg3 * np.minimum(arg1, arg2)\n",
    "\n",
    "\n",
    "temp_learning_rate_schedule = NoamDecayScheduler({\"d_model\": 512, \"warmup_steps\": 4000})\n",
    "#下面是学习率的设计图\n",
    "plt.plot(temp_learning_rate_schedule(np.arange(0, 40000)))\n",
    "plt.ylabel(\"Leraning rate\")\n",
    "plt.xlabel(\"Train step\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "XHj_EjzlN4yW"
   },
   "source": [
    "### 优化器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "id": "1EVLKx2rN4yW",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:27.240054200Z",
     "start_time": "2024-08-05T08:06:27.161089200Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.optim.lr_scheduler import LambdaLR\n",
    "from torch.optim import Adam\n",
    "\n",
    "def get_optimizer(model, config):\n",
    "    base_lr = 0.1\n",
    "    beta1 = config[\"beta1\"] # Adam 的 beta1\n",
    "    beta2 = config[\"beta2\"] # Adam 的 beta2\n",
    "    eps = config[\"eps\"]\n",
    "    optimizer = Adam(model.parameters(), lr=base_lr, betas=(beta1, beta2), eps=eps)\n",
    "    lr_scheduler = NoamDecayScheduler(config) #config是一个字典，包含了学习率衰减的参数\n",
    "    # 使用 LambdaLR 调度器，它可以根据给定的函数 lr_lambda 调整学习率。这里将 lr_scheduler 作为函数传递给 LambdaLR，它包含了特定于模型或任务的学习率调度规则\n",
    "    scheduler = LambdaLR(optimizer, lr_lambda=lr_scheduler)\n",
    "    return optimizer, scheduler"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JBhiO7O2N4yW"
   },
   "source": [
    "### Callback"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "id": "rWFMJwBkN4yX",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:36.478359Z",
     "start_time": "2024-08-05T08:06:27.161089200Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "\n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\",\n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "\n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "\n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "\n",
    "        )\n",
    "\n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "id": "64y_NBHMN4yX",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:36.486389200Z",
     "start_time": "2024-08-05T08:06:36.483880700Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch.\n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = - np.inf\n",
    "\n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "\n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "\n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "id": "Uk4PEb70N4yX",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:36.523266500Z",
     "start_time": "2024-08-05T08:06:36.494383100Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = - np.inf\n",
    "        self.counter = 0\n",
    "\n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter\n",
    "            self.counter = 0\n",
    "        else:\n",
    "            self.counter += 1\n",
    "\n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_AB84Qx1N4yX"
   },
   "source": [
    "### training & valuating"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "id": "1mKPSFkON4yX",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:36.525265400Z",
     "start_time": "2024-08-05T08:06:36.503642Z"
    }
   },
   "outputs": [],
   "source": [
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    for batch in dataloader:\n",
    "        encoder_inputs = batch[\"encoder_inputs\"]\n",
    "        encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "        decoder_inputs = batch[\"decoder_inputs\"]\n",
    "        decoder_labels = batch[\"decoder_labels\"]\n",
    "        decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "        # 前向计算\n",
    "        outputs = model(\n",
    "            encoder_inputs=encoder_inputs,\n",
    "            decoder_inputs=decoder_inputs,\n",
    "            encoder_inputs_mask=encoder_inputs_mask\n",
    "            )\n",
    "        logits = outputs.logits\n",
    "        loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)         # 验证集损失\n",
    "        loss_list.append(loss.cpu().item())\n",
    "\n",
    "    return np.mean(loss_list)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "id": "PClBmtgWN4yY",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:36.549252Z",
     "start_time": "2024-08-05T08:06:36.514271Z"
    }
   },
   "outputs": [],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler=None,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "\n",
    "    global_step = 1\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for batch in train_loader:\n",
    "                encoder_inputs = batch[\"encoder_inputs\"]\n",
    "                encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "                decoder_inputs = batch[\"decoder_inputs\"]\n",
    "                decoder_labels = batch[\"decoder_labels\"]\n",
    "                decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "\n",
    "                # 前向计算\n",
    "                outputs = model(\n",
    "                    encoder_inputs=encoder_inputs,\n",
    "                    decoder_inputs=decoder_inputs,\n",
    "                    encoder_inputs_mask=encoder_inputs_mask\n",
    "                    )\n",
    "                logits = outputs.logits\n",
    "                loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)\n",
    "\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                if scheduler is not None:\n",
    "                    scheduler.step() # 更新学习率\n",
    "\n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"step\": global_step\n",
    "                })\n",
    "\n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "\n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    cur_lr = optimizer.param_groups[0][\"lr\"] if scheduler is None else scheduler.get_last_lr()[0]\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step,\n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            lr=cur_lr,\n",
    "                            )\n",
    "\n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=-val_loss)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(-val_loss)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "\n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "            pbar.set_postfix({\"epoch\": epoch_id, \"loss\": loss, \"val_loss\": val_loss})\n",
    "\n",
    "    return record_dict\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "khZqMS8pN4yY",
    "outputId": "2367a363-585b-4321-f7ea-cb11d9623008",
    "ExecuteTime": {
     "end_time": "2024-08-05T08:06:36.628920Z",
     "start_time": "2024-08-05T08:06:36.541257800Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16\\.cache\\de2en_train_128.npy\n",
      "load val dataset from wmt16\\.cache\\de2en_val_128.npy\n"
     ]
    }
   ],
   "source": [
    "#模型的超参\n",
    "config = {\n",
    "    \"bos_idx\": 1,\n",
    "    \"eos_idx\": 3,\n",
    "    \"pad_idx\": 0,\n",
    "    \"vocab_size\": len(word2idx),\n",
    "    \"max_length\": 128,\n",
    "    \"d_model\": 512,\n",
    "    \"dim_feedforward\": 2048, # FFN 的隐藏层大小\n",
    "    \"dropout\": 0.1,\n",
    "    \"layer_norm_eps\": 1e-6, # 层归一化的 epsilon, 防止除零错误\n",
    "    \"num_heads\": 8,\n",
    "    \"num_decoder_layers\": 6,\n",
    "    \"num_encoder_layers\": 6,\n",
    "    \"label_smoothing\": 0.1,\n",
    "    \"beta1\": 0.9, # Adam 的 beta1\n",
    "    \"beta2\": 0.98,\n",
    "    \"eps\": 1e-9,\n",
    "    \"warmup_steps\": 4_000,\n",
    "    \"share_embedding\": False, # 是否共享词向量\n",
    "    }\n",
    "\n",
    "\n",
    "def get_dl(dataset, batch_size, shuffle=True):\n",
    "    sampler = TransformerBatchSampler(dataset, batch_size=batch_size, shuffle_batch=shuffle)\n",
    "    sample_dl = DataLoader(dataset, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer))\n",
    "    return sample_dl\n",
    "\n",
    "# dataset\n",
    "train_ds = LangPairDataset(\"train\", max_length=config[\"max_length\"])\n",
    "val_ds = LangPairDataset(\"val\", max_length=config[\"max_length\"])\n",
    "# tokenizer\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word, max_length=config[\"max_length\"])\n",
    "batch_size = 2048\n",
    "# dataloader\n",
    "train_dl = get_dl(train_ds, batch_size=batch_size, shuffle=True)\n",
    "val_dl = get_dl(val_ds, batch_size=batch_size, shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模型参数量: 59038198\n"
     ]
    }
   ],
   "source": [
    "#计算模型参数量\n",
    "model = TransformerModel(config)\n",
    "print(f\"模型参数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-08-05T08:52:53.233851300Z",
     "start_time": "2024-08-05T08:52:51.458743700Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "{'bos_idx': 1,\n",
       " 'eos_idx': 3,\n",
       " 'pad_idx': 0,\n",
       " 'vocab_size': 9718,\n",
       " 'max_length': 128,\n",
       " 'd_model': 512,\n",
       " 'dim_feedforward': 2048,\n",
       " 'dropout': 0.1,\n",
       " 'layer_norm_eps': 1e-06,\n",
       " 'num_heads': 8,\n",
       " 'num_decoder_layers': 6,\n",
       " 'num_encoder_layers': 6,\n",
       " 'label_smoothing': 0.1,\n",
       " 'beta1': 0.9,\n",
       " 'beta2': 0.98,\n",
       " 'eps': 1e-09,\n",
       " 'warmup_steps': 4000,\n",
       " 'share_embedding': False}"
      ]
     },
     "metadata": {},
     "execution_count": 51
    }
   ],
   "source": [
    "config"
   ],
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-05-07T08:52:38.372364400Z",
     "start_time": "2024-05-07T08:52:38.191467100Z"
    },
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "63slV04gWLn8",
    "outputId": "5c5ede03-0f6f-48d5-a177-d961886ec340"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 49,
     "referenced_widgets": [
      "55146ac7395840dca847e0f63aa80561",
      "abb9bc76c0ce4634bd340be20c074b8c",
      "a89d790df59245fcaf26335d4903c9b4",
      "f837255914194dc792cf1514dee03dc5",
      "46a51a1719d8444eb5877720253d3c37",
      "013d2a6b90144432bbfb15136235faba",
      "2b82e42c34804b13bc83be4c7009d4e2",
      "9233439bdd6746c28ad4a20ef47799f6",
      "0e10428326bc436e8dbc079e89570bde",
      "0fe4ed7283c04496affe727c235d31c4",
      "0c0c7229347b49ffbb9a0f7bd41199b9"
     ]
    },
    "id": "tVO2zJ04N4yY",
    "outputId": "7d4079f5-65d4-4da1-9001-91aa5525250e"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  0%|          | 0/280 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "55146ac7395840dca847e0f63aa80561"
      }
     },
     "metadata": {}
    }
   ],
   "source": [
    "epoch = 20\n",
    "\n",
    "# model\n",
    "model = TransformerModel(config)\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer, scheduler = get_optimizer(model, config)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "exp_name = \"translate-transformer-{}\".format(\"share\" if config[\"share_embedding\"] else \"not-share\")\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "# tensorboard_callback.draw_model(model, [1, MAX_LENGTH])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\n",
    "    f\"checkpoints/{exp_name}\", save_step=500, save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=8)\n",
    "\n",
    "model = model.to(device)\n",
    "\n",
    "record = training(\n",
    "    model,\n",
    "    train_dl,\n",
    "    val_dl,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler,\n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500\n",
    "    )\n",
    "\n",
    "# Training took 3.5 days on 8 P100 GPUs\n",
    "# We trained the base models for a total of 100,000 steps or 12 hours. For our big models,(described on the bottom line of table 3), step time was 1.0 seconds. The big models were trained for 300,000 steps (3.5 days)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rTjbeFlIN4yZ"
   },
   "source": [
    "## 推理\n",
    "\n",
    "- 翻译项目的评估指标一般是BLEU4，感兴趣的同学自行了解并实现\n",
    "- 接下来进行翻译推理，并作出注意力的热度图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "WxsjkyQgN4yZ",
    "outputId": "5ce160c2-ce7e-4bc3-d2b6-a332c7fcf6a1"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Collecting fastBPE\n",
      "  Downloading fastBPE-0.1.0.tar.gz (35 kB)\n",
      "  Preparing metadata (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
      "Building wheels for collected packages: fastBPE\n",
      "  Building wheel for fastBPE (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
      "  Created wheel for fastBPE: filename=fastBPE-0.1.0-cp310-cp310-linux_x86_64.whl size=806588 sha256=b77d4a1091f1aa812ca7c62cb09e4397a6cb8ae13061a3c1aeb5bc104e3d8dc7\n",
      "  Stored in directory: /root/.cache/pip/wheels/13/5d/b9/4b8897941ebc9e8c6cc3f3ffd3ea5115731754269205098754\n",
      "Successfully built fastBPE\n",
      "Installing collected packages: fastBPE\n",
      "Successfully installed fastBPE-0.1.0\n"
     ]
    }
   ],
   "source": [
    "# !pip install Cython  # if failed to install fastBPE, try this line\n",
    "!pip install fastBPE #分词使用\n",
    "# 在 Windows 系统上并没有 sys/mman.h 文件"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "exp_name"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 35
    },
    "id": "f6Ry1PcAn4k1",
    "outputId": "6393524e-e5c3-4f93-dd9d-f249ca52bc05"
   },
   "execution_count": null,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'translate-transformer-not-share'"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      }
     },
     "metadata": {},
     "execution_count": 54
    }
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "id": "jT1Mqiq3N4yZ",
    "ExecuteTime": {
     "end_time": "2024-08-05T03:10:32.326542400Z",
     "start_time": "2024-08-05T03:10:31.006376300Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "\n",
    "state_dict = torch.load(f\"best.ckpt\", map_location=\"cpu\")\n",
    "\n",
    "# state_dict1 = torch.load(\"epoch125-step132426.ckpt\", map_location=\"cpu\")\n",
    "# state_dict = state_dict1[\"state_dict\"]\n",
    "\n",
    "# update keys by dropping `model`\n",
    "# for key in list(state_dict):\n",
    "#     state_dict[key.replace(\"model.\", \"\")] = state_dict.pop(key)\n"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "!pip install nltk"
   ],
   "metadata": {
    "id": "eKwIDdjgvTlO",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "outputId": "35979ff2-a16d-4aa4-881a-8496e39e2469",
    "ExecuteTime": {
     "end_time": "2024-08-05T03:10:46.720508800Z",
     "start_time": "2024-08-05T03:10:36.179313900Z"
    }
   },
   "execution_count": 52,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Defaulting to user installation because normal site-packages is not writeable\n",
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Collecting nltk\n",
      "  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/a6/0a/0d20d2c0f16be91b9fa32a77b76c60f9baf6eba419e5ef5deca17af9c582/nltk-3.8.1-py3-none-any.whl (1.5 MB)\n",
      "Requirement already satisfied: click in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (8.1.7)\n",
      "Requirement already satisfied: joblib in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (1.4.2)\n",
      "Requirement already satisfied: regex>=2021.8.3 in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (2024.7.24)\n",
      "Requirement already satisfied: tqdm in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from nltk) (4.66.4)\n",
      "Requirement already satisfied: colorama in c:\\users\\administrator\\appdata\\roaming\\python\\python312\\site-packages (from click->nltk) (0.4.6)\n",
      "Installing collected packages: nltk\n",
      "Successfully installed nltk-3.8.1\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "[notice] A new release of pip is available: 24.1.2 -> 24.2\n",
      "[notice] To update, run: python.exe -m pip install --upgrade pip\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "!rm -r wmt16/.cache"
   ],
   "metadata": {
    "id": "WVksjIhL_pt9"
   },
   "execution_count": 55,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "tokenizer.decode([[   5,   16,    6,   23,  150,   80, 8248,   35,  232,    4,    3]])"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "2MY69-6WWSjp",
    "outputId": "911d9066-7917-4917-b5cc-d679a1c63865"
   },
   "execution_count": 38,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "['a man in an orange hat starring at something .']"
      ]
     },
     "metadata": {},
     "execution_count": 38
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "from nltk.translate.bleu_score import sentence_bleu\n",
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "# from dataset import LangPairDataset\n",
    "test_ds = LangPairDataset(\"test\", max_length=128, data_dir=\"./wmt16\")\n",
    "test_dl = DataLoader(test_ds, batch_size=1, collate_fn=partial(collate_fct, tokenizer=tokenizer))\n",
    "\n",
    "model = model.to(device)\n",
    "model.eval()\n",
    "collect = {}\n",
    "loss_collect = []\n",
    "\n",
    "predictions = []\n",
    "answers = []\n",
    "# 初始化BLEU分数列表\n",
    "bleu_scores = []\n",
    "for idx, batch in tqdm(enumerate(test_dl)):\n",
    "    encoder_inputs = batch[\"encoder_inputs\"]\n",
    "    encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "    decoder_inputs = batch[\"decoder_inputs\"]\n",
    "    decoder_labels = batch[\"decoder_labels\"]\n",
    "    # print(decoder_labels.cpu())\n",
    "    # decoder_labels1=tokenizer.decode(decoder_labels.cpu().numpy())\n",
    "    # print(decoder_labels1)\n",
    "    # 前向计算\n",
    "    outputs = model(\n",
    "        encoder_inputs=encoder_inputs,\n",
    "        decoder_inputs=decoder_inputs,\n",
    "        encoder_inputs_mask=encoder_inputs_mask\n",
    "        )\n",
    "    loss = loss_fct(outputs.logits, decoder_labels)         # 验证集损失\n",
    "\n",
    "    # print(outputs.logits.shape, decoder_labels.shape)\n",
    "\n",
    "    # loss = loss_fct(outputs.logits[:, :decoder_labels.shape[1]], decoder_labels)         # 验证集损失\n",
    "    # outputs = model.infer(encoder_inputs=encoder_inputs)\n",
    "    # print(outputs.logits.shape)\n",
    "    preds = outputs.logits.argmax(dim=-1) # 预测结果，[1,seq_len]\n",
    "    # print(preds.shape)\n",
    "    #把preds转为英文单词\n",
    "    preds = tokenizer.decode(preds.cpu().numpy()) #['预测句子']\n",
    "    # predictions.append(preds)\n",
    "    # print(preds)\n",
    "    #把decoder_labels转为英文单词\n",
    "    decoder_labels = tokenizer.decode(decoder_labels.cpu().numpy()) #['标签句子']\n",
    "    # answers.append(decoder_labels)\n",
    "    # print(decoder_labels)\n",
    "    belu=sentence_bleu([decoder_labels[0].split()],preds[0].split(),weights=(1, 0, 0, 0))\n",
    "    bleu_scores.append(belu)\n",
    "    collect[idx] = {\"loss\": loss.item(), \"src_inputs\": encoder_inputs, \"trg_inputs\": decoder_inputs, \"mask\": encoder_inputs_mask, \"trg_labels\": decoder_labels, \"preds\": preds}\n",
    "    loss_collect.append(loss.item())\n",
    "    # break\n",
    "\n",
    "# sort collect by value\n",
    "collect = sorted(collect.items(), key=lambda x: x[1][\"loss\"])\n",
    "print(f\"testing loss: {np.array(loss_collect).mean()}\")\n",
    "sum(bleu_scores) / len(bleu_scores)"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101,
     "referenced_widgets": [
      "c9db4cd83b7b4f53a0fa93b8c365debd",
      "a972aba7913b48c6973ae27bff5b9d1d",
      "22aa774ab39e4fd78b0ff08d430d2b5d",
      "9a01f02fb99b4b81ac487d23bc5daf4a",
      "19f846338f6b48c9930b58575641e5b8",
      "7d293204dc1b4d97bb5ee670787c7b7d",
      "ea8fa440f320435aa75c9bef75bc3f08",
      "4e64bcb87e584beda0408a0aa5a659ae",
      "b077d7562d814df5b6d839464dd4122f",
      "5a28babf4e2e4393bdc1a093034885ab",
      "be07d518c21149d196c610fd57b1eaec"
     ]
    },
    "id": "5QgodlOJfgKj",
    "outputId": "2907bad2-7cb1-4295-c8b0-77ac0dabf398"
   },
   "execution_count": 46,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "load test dataset from wmt16/.cache/de2en_test_128.npy\n"
     ]
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "0it [00:00, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "c9db4cd83b7b4f53a0fa93b8c365debd"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "testing loss: 3.258438676626829\n"
     ]
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "0.5708733100076216"
      ]
     },
     "metadata": {},
     "execution_count": 46
    }
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 535,
     "referenced_widgets": [
      "40f7e3754ad44b33acfe0a80b3648a3a",
      "b1e51beac50f4947a9dd235d8aa87603",
      "8cd9e9702e114ebc856f56975544ede8",
      "b7c7a718a8654956947108969557ca83",
      "20a27d1ec76f4d85b1ffe99401fbb5b7",
      "e0ddf3970b3e4248bc87d4649fa9c448",
      "d7bdde54e2534c49a207540bd258d3ae",
      "22484000400842f0b6e245dc610acbba",
      "a66a5b4ff37d4dafa4ba3081aa1359f4",
      "0b7866c29e5f4afe9b92de15f1b88c3e",
      "333e84a304184c57af1821620ae80f6a"
     ]
    },
    "id": "KGSym4CbN4ya",
    "outputId": "f5e9c4c2-c92e-44e6-c83a-5cb720305428",
    "ExecuteTime": {
     "end_time": "2024-05-06T13:16:14.099749Z",
     "start_time": "2024-05-06T13:16:13.991811900Z"
    }
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  0%|          | 0/128 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "40f7e3754ad44b33acfe0a80b3648a3a"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "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f/NLrh0+/PBDPfvss3ryySfPuaxNmzb1extyc3N15MiRiz6EESVXSVifSqOCggIdP378os57vBQUPGfo2bOnqlSp4j2eet++fWrYsKGqVKmiOnXq6Ouvv9bUqVPVq1cvm1sa2DZt2qQbbrhBLVq00IIFC3TbbbcV2S1fp04du5sIlChXXHHFRR9L/v333/u5NbgYdvYntWrVOu9jLpdLO3bs8HmmXYKCzr7CxqkTo518knlhYaEGDx6s+fPn64orrtCECROKdRglzq20rk9W+c9//qOsrCz17t3be98LL7ygMWPGqKCgQNdff70++OADxcTE+DSXQ9rOsGrVKk2bNs17+5133lFsbKzWr1+vkJAQvfzyy/rHP/5BwXOZ7Not/8ADDygtLU0RERFF7j969Kj69++vt956y2/ZpUVmZqYSExPP+nJujNHu3btVvXp1m1oW2Lp27Wp3E3hvi8nO/mTnzp0+n2dJZdey7t27V0OGDNH8+fO1b9++s0ae8tUX40OHDumJJ57Q3LlzNW3aNHXo0EGSNGDAAH366afq27ev5syZo/79+2vmzJk+ySzNStNnxw4TJkzQX//6V+/tFStWaNSoUXruuefUqFEjPf300xozZowmTJjg01z28JyhbNmy+t///uc9BKFTp05q0qSJXnzxRUlSRkaG2rZtq6ysLDubGfDO98Xp1GP++uIUHBysPXv2nHXYwf79+xUfH6+CggK/5JYm53uNs7KyVKlSJX4dC2C8t8VDf+JsHTt2VGZmph5//HElJCSc1Z916dLFJzl33323srKydM011yg9PV3vvfeePvnkE915551atmyZWrdurR9++EHXX3+9fv/9d59knguff/hCpUqVNHfuXO+Q7oMHD1Z6err3wrmzZ8/WgAEDtHXrVp/msofnDJGRkTp06JC3g1qzZo369Onjfdzlcvn12juFhYWaNm2a9xejMy8Wt2DBAr9lW6lWrVrn3XDWqlXL5xvOnJwcGWNkjNHhw4dVpkwZ72OFhYWaPXs2x177yPkuHnvkyJEirzsCD+9t8djZn5SWvuSUd999V//85z+1c+dOrVy5UjVq1NCkSZNUq1YtnxUeZ1q2bJmWLl2q5s2b+2X+p8yZM0crVqxQrVq1dOWVVyomJkY5OTkaOXKkWrduLUkKDw/XsWPH/NqO8/0+npeXp7CwML9mW82O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/KzPw4EDB/ySW7t2bb355pvq0KFDkVG13nnnHY0bN07p6ek+z7z22mvVo0cP9e/fXxEREdq4caNq1aql/v37a+vWrZozZ47PM0+x4/O/YcOGIrfz8/O1bt06TZgwQS+88MIfXtPvUti1PknSu+++6z0nbOXKlapRo4YmTpyo2rVrq0uXLn7LtdqJEyfOeUHmO++8U4MGDfLLBbQpeC7SiRMndOLECb9f6dbj8cjj8XhHXfnggw+0fPly1atXT4888ojfrspthZiYmCJf9I8ePWr5BuXxxx/XtGnTdMstt5yz8Jg4caJfcsuVK6f//ve/atiwoV/mX5J06tRJxhi99957io2NlSRlZWXpnnvuUVBQkL766iubWxj4nn/+eY0aNUoNGjRQ5cqVzxq0wF+FO++tb1jRnzi5LznTqRP4z+e+++7zS25qaqr+/e9/66233tKNN96o2bNn66efftKgQYM0cuRI9e/f3+eZy5YtU8eOHXXPPfd4By9JT0/XihUrtHjxYrVs2dLnmaeUpM//V199pZdeekmLFi3y+bztWp/eeOMNjRo1SgMHDtTzzz+vH3/8UbVr19a0adM0ffp0LVy40C+5pQUFzzm8/fbb+v7773XllVfq7rvv1vDhwzV+/HgVFBTo+uuv18yZMxUXF+e3/OPHj2vjxo1n7YFwuVx+vaigv/3RRuR0/tqgVKhQQe+8847lI5786U9/0sSJE/0yak9JU65cOa1atUrJyclF7t+wYYOuuuoqHTlyxKaWOUdMTIwmTpyo3r17W5rLe1t8dvYnTu1LSgpjjMaOHavU1FTvsOxut1tDhgzRmDFj/Ja7Y8cOpaamasOGDTpy5IhatGihp5566qzPpa+VpM//tm3b1KxZMx09etSyTH9LSkrS2LFjvRcgPbXHcNOmTUpJSdH+/fvtbqJPrFmzRi1btjzvYZB5eXn6/PPPdfvtt/s22OdnBQW4559/3pQtW9Z06NDBxMbGmkceecTEx8ebcePGmRdffNFUq1bNPPLII37L//rrr02FChWMy+U6awoKCvJbrtV69eplpk6davmJ1wkJCWbLli2WZhpjzPz5803btm3NwoULzf79+012dnaRyUliYmLM8uXLz7p/2bJlJiYmxoYWOU/lypVNRkaG5bm8t8VjZ3/i9L7k9O3mmdtTq7eveXl55scffzSrV682hw8f9mtWr169zFtvvWXLoCV2fP7PfC8PHTpkNm/ebO644w7TrFkzn+acL9Oq9alMmTLegUZOH5AiIyPDlClTxm+5VgsKCioy6lxERIR3WY0x5rfffvPLNopBC84wbdo0TZ06VT179tTatWvVpk0bzZo1y3ul7CZNmuiRRx7xW37//v11++23a9SoURccp9zXtm7dqoULF57zvJZRo0b5PM/tdmvcuHHq27evqlSpovbt2yslJUXt27f36QXMzvTEE08oLS1Nr732mqXXaujQoYMk6frrry+Saxw4aEHnzp310EMPaerUqd6LLq5evVqPPPKIbrvtNr9mW70e22XAgAF69dVX//C6Ur5m53sbiOzsT+zqSyRrPocxMTHas2ePKlWqpOjo6HNuz63avoaFhSkpKcmvGadnpaam6sEHH7S075Ts+fyf6701xigxMVEzZ870WU5JWJ9q1aql9evXq0aNGkXunzNnjho1auSXTDuYMw4sO/P2+e67XBzSdga3261t27Z5LxTndru1ceNG7wl6v/zyi2rVqnXWVat9JTIyUuvWrbP05N/Jkyfr0UcfVYUKFRQfH3/W+QD+vCL3L7/8oiVLlmjx4sVavHixMjIylJCQoJ9//tlnGWee1LhgwQLFxsaqcePGZx3H/sknn/gs93SLFy++4ON2XrHb1w4dOqT77rtP//nPf7yvb35+vrp06aJp06YpKirKL7l2rsdW69atmxYsWKC4uDhL12O73ttAZWd/YkdfIln3OVy8eLF3MBgrt6/FOUneX59DyZq+80x2fP7PfG+DgoJUsWJF1a1b13t+mq9y7FifTjdlyhQ9++yzGj9+vPr06aMpU6Zo+/btSk1N1ZQpU3TnnXf6JddqQUFB+u2337yjDZ5++J508sL0VapU8XlhyR6eM+Tn5xcZHSIsLKzIl4mQkBC//lr017/+VYsWLbK0k3r++ef1wgsv6KmnnrIs85SYmBjFxcUpJiZG0dHRCgkJUcWKFX2aceZGuFu3bj6d/8Vo3769Dh06pKlTp3qH80xKSlKfPn38/iUxPT1dmZmZZ32p8ucvcp9//rm2bdvmHaUoKSlJdevW9UveKXaux3a8xv4Ynehicu14bwOVnf2JHX2JZN3n8PQvne3bt9fSpUv1r3/9S9u3b9dHH32kqlWr6t1331WtWrV8mnux22t/H0FgRd95Jjs+/6fe59O3sQcPHvSO7OWrbaxd69PpHnzwQZUtW1YjRoxQbm6u7rrrLlWtWlVpaWmOKXbsRMFzDunp6frtt98kndyt9r///c97Mp6/Txp77bXX1KNHDy1duvScw83+7W9/83nmwYMH1aNHD5/P90KGDx+uRYsWad26dWrUqJHat2+voUOH6tprr1VMTIxPs95++23v/48dOyaPx6Ny5cpJknbt2qXPPvtMjRo10s033+zT3NOtXbtWf/7zn1WmTBnvoQATJ07U2LFjNW/ePLVo0cLnmTt27FC3bt30ww8/yOVyeXcRn+qI/Vm4T506VRMnTtTWrVslSfXq1dPAgQP14IMP+i3TjvXYrtf49ddft2U9lux5bwOZXf2JHX2JZM/n8OOPP1avXr109913a926dcrLy5MkZWdna+zYsZo9e7bPsk7vT2bMmKGePXue83lPPvmkzzJPZ2XfeS5Wf/537Nih7t27a+PGjZZtY61cn0537NgxdevWTXfffbdyc3O1adMmLV++XNWqVfNLnp1s2S76/KygAHfqhM7znejp7xM+p0yZYkJCQkz58uVNjRo1TM2aNb1TrVq1/JL5wAMPmDfeeMMv8z4fl8tlKlWqZFJTUy0dRODGG2/0LuvBgwdN5cqVTbVq1UyZMmXM66+/7rfcq6++2vTu3dvk5+d778vPzzf33Xefueaaa/yS2blzZ9OlSxfz+++/m/Lly5v09HSzdOlS07p1a7NkyRK/ZBpjzMiRI025cuXM0KFDzeeff24+//xzM3ToUFO+fHm/XD35FDvWY7teY7vWY7ve20BlZ39iR19ijD2fw+bNm5vp06cbY4qe7P3999+bypUr+y03KirKzJ49+6z7Bw0aZOLj4/2SaVffaYw9n/8zt7E//vij37exdq1Pdm3XrWbXdpGC5wy7du26qMlfKleubF544QVTWFjot4wzjR071lSoUMHcd9995uWXXzZpaWlFJn9Yv369SUtLM926dTMVKlQwVapUMT179jT/+te//LoRj4uLM5s2bTLGGDN58mTTtGlTU1hYaGbNmmUaNmzot9wyZcqYzZs3n3X/jz/+aMqWLeuXzLi4OLNhwwZjjDGRkZHmf//7nzHm5IhxzZs390umMcZUqFDBvP/++2fd//7775u4uDi/5dqxHtv1Gtu1Htv13gYqO/sTO/oSY+z5HJYtW9bs3LnTGFP0C+r27duN2+32S6Yxxnz55ZcmKirKLF261Hvf448/bhISEs65vfcFu/pOY+z5/NuxjbVrfbJru241u7aLFDyn2bBhQ7E6h02bNhX5xd4XYmJiLB9u8vRf/s6c/PlL4OnWr19v7rvvPhMSEuLXPWhly5Y1P/30kzHGmB49ephnn33WGGNMZmam3woPY4ypVKmSmTt37ln3z5kzx1SqVMkvmdHR0WbHjh3GGGNq165tFixYYIwxZtu2bX5d1qioqHMOmbxlyxYTFRXlt1w71mO7XmO71mO73ttAZHd/YkdfYow9n8NatWqZb775xhhT9Avq9OnTTaNGjfySecp7771nYmJizNq1a82jjz5qqlSpYumeF6v6TmPs+fzbsY21a32ya7tuJTu3ixQ8pwkKCjL79u276OefOXa4LwwcONC88MILPp1nSeTxeMx///tfM378eHPrrbeamJgYExwcbK644gozcOBAv+UmJyebtLQ0k5mZaSIjI82KFSuMMcasXbvWr7uq+/fvb6pVq2ZmzpxpMjMzTWZmppkxY4apVq2aGTBggF8yr776avPpp58aY4zp2bOn+fOf/2yWLVtm7r33XtO4cWO/ZBpz8hfOQYMGnXX/E088YR577DG/5drBrtfYrvW4NL23l8vu/qS09CXGnNyrlJSUZFatWmUiIiLM0qVLzb///W9TsWJF88orr/g9/x//+Idxu92mWrVqZuvWrX7NsqvvNMaez78d21i71ie7tutWsnO7yKAFpzHGaOTIkQoPD7+o5/tjKNHCwkK9+OKLmjt3rpo2bXrWiaYTJkzwSc7gwYM1ZswYlStXToMHDz7v81wul8aPH++TzNPFxsbqyJEjatasmdq3b6++ffvqmmuuUXR0tM+zTjdq1CjdddddGjRokG644Qa1bdtWkjRv3jxdccUVfst9+eWX5XK5dO+996qgoECSFBoaqkcffVTjxo3zS+aIESO8V6F+7rnn1LlzZ11zzTWKi4vTBx984NOs09chl8ulKVOmaN68ebryyislnbxWQ2Zmpu69916f59q5Hlv5Gp/OyvXYrvf2YnTo0EE7duzQjh07LM/+I3b3J1b1JZL9n8OhQ4fK4/HohhtuUG5urq699lq53W4NGTJE/fv392nW+ZavYsWKatGihV5//XXvfb58jU+xuu+0+/NvxzbWyvXpdHZ9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\n"
     },
     "metadata": {}
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "['man in white shirt on a boat in a small boat.']"
      ]
     },
     "metadata": {},
     "execution_count": 41
    }
   ],
   "source": [
    "import re\n",
    "from fastBPE import fastBPE\n",
    "from sacremoses import MosesDetokenizer, MosesTokenizer\n",
    "\n",
    "# `MosesTokenizer` 和 `MosesDetokenizer` 是来自 `sacremoses` 库的工具，用于自然语言处理中的分词（Tokenization）和去标记化（Detokenization）。这些工具主要用于对文本进行预处理和后处理，通常在处理自然语言处理任务时会用到。\n",
    "#\n",
    "# ### MosesTokenizer：\n",
    "# - **作用**：将原始文本分割成单词和标点符号。\n",
    "# - **特点**：基于 Moses 翻译工具中使用的分词方法。\n",
    "# - **功能**：\n",
    "#   - 将句子分割成单词和标点符号。\n",
    "#   - 处理缩写、连字符、标点等特殊情况。\n",
    "#   - 对文本进行标记化，方便后续处理。\n",
    "#\n",
    "# ### MosesDetokenizer：\n",
    "# - **作用**：将分词后的文本重新组合成原始的句子。\n",
    "# - **特点**：用于对分词后的文本进行还原，使其恢复为可读的句子形式。\n",
    "# - **功能**：\n",
    "#   - 将分词后的单词和标点符号重新组合成句子。\n",
    "#   - 处理分词后的标点、缩写等情况，使得结果更加自然和可读。\n",
    "#\n",
    "# 这些工具通常在文本预处理和后处理过程中使用，对输入的文本进行标记化和去标记化，是一种常用的处理方式。在自然语言处理任务中，对文本进行正确的分词和还原是很重要的，而 `MosesTokenizer` 和 `MosesDetokenizer` 提供了方便、高效的工具来处理这些任务。\n",
    "\n",
    "class Translator:\n",
    "    def __init__(self, model, src_tokenizer, trg_tokenizer):\n",
    "        self.bpe = fastBPE(\"./wmt16/bpe.10000\", \"./wmt16/vocab\")\n",
    "        self.mose_tokenizer = MosesTokenizer(lang=\"de\")\n",
    "        self.mose_detokenizer = MosesDetokenizer(lang=\"en\")\n",
    "        self.model = model\n",
    "        self.model.eval()\n",
    "        self.src_tokenizer = src_tokenizer\n",
    "        self.trg_tokenizer = trg_tokenizer\n",
    "        self.pattern = re.compile(r'(@@ )|(@@ ?$)')\n",
    "\n",
    "    def draw_attention_map(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "        attn_scores (numpy.ndarray): 表示自注意力机制（self-attention）分数。\n",
    "        cross_attn_scores (numpy.ndarray): 表示交叉注意力机制的注意力分数。\n",
    "        src_words_list (list): 源语言句子的单词列表。\n",
    "        trg_words_list (list): 目标语言句子的单词列表。\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "\n",
    "        fig = plt.figure(figsize=(10, 5), constrained_layout=True) # constrained_layout=True 自动调整子图参数，使之填充整个图像区域\n",
    "        grid = plt.GridSpec(trg_len, trg_len + src_len, wspace=0.1, hspace=0.1)# wspace,hspace 控制子图之间的间距\n",
    "        #下面是attn_scores的热力图\n",
    "        self_map = fig.add_subplot(grid[:,:trg_len]) #  添加子图\n",
    "        self_map.matshow(attn_scores.mean(dim=0), cmap='viridis') # 绘制热力图，cmap表示颜色,dim=0表示对第0维求均值\n",
    "        self_map.set_yticks(range(trg_len), trg_words_list, fontsize=10)\n",
    "        self_map.set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "        #下面是cross_attn_scores的热力图\n",
    "        cross_map = fig.add_subplot(grid[:, trg_len:])\n",
    "        cross_map.matshow(cross_attn_scores.mean(dim=0), cmap='viridis')\n",
    "        cross_map.set_yticks(range(trg_len), [], fontsize=6)\n",
    "        cross_map.set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    def draw_attention_maps(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list, heads_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "\n",
    "        Args:\n",
    "            - scores (numpy.ndarray): shape = [source sequence length, target sequence length]\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "        # cross_attn_scores = cross_attn_scores[:, :len(src_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "        fig, axes = plt.subplots(2, len(heads_list), figsize=(5 * len(heads_list), 10))\n",
    "        for i, heads_idx in enumerate(heads_list):\n",
    "            axes[0, i].matshow(attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[0, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[0, i].set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "            axes[0, i].set_title(f\"head {heads_idx}\")\n",
    "            axes[1, i].matshow(cross_attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[1, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[1, i].set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "            axes[1, i].set_title(f\"head {heads_idx}\")\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "\n",
    "    def __call__(self, sentence_list, heads_list=None, layer_idx=-1):\n",
    "        # 将输入句子列表转换为小写，并使用 MosesTokenizer 进行分词处理。\n",
    "        sentence_list = [\" \".join(self.mose_tokenizer.tokenize(s.lower())) for s in sentence_list]\n",
    "        # 将分词后的结果进行 BPE 编码，得到 tokens_list。\n",
    "        tokens_list = [s.split() for s in self.bpe.apply(sentence_list)]\n",
    "        # 使用 src_tokenizer 对 tokens_list 进行编码，同时添加起始标记 ([BOS]) 和结束标记 ([EOS])。\n",
    "        encoder_input, attn_mask = self.src_tokenizer.encode(\n",
    "            tokens_list,\n",
    "            add_bos=True,\n",
    "            add_eos=True,\n",
    "            return_mask=True,\n",
    "            )\n",
    "        encoder_input = torch.Tensor(encoder_input).to(dtype=torch.int64)\n",
    "        # 使用模型的 infer 方法对编码器输入进行推理，得到输出结果 outputs\n",
    "        outputs = model.infer(encoder_inputs=encoder_input, encoder_inputs_mask=attn_mask)\n",
    "\n",
    "        preds = outputs.preds.numpy()\n",
    "        # 使用目标语言的 trg_tokenizer 对预测序列进行解码，得到解码后的目标语言句子列表 trg_decoded。\n",
    "        trg_decoded = self.trg_tokenizer.decode(preds, split=True, remove_eos=False, remove_bos=False, remove_pad=False)\n",
    "        # 使用源语言的 src_tokenizer 对编码器输入进行解码，得到解码后的源语言句子列表 src_decoded。为下面绘制热力图做准备。\n",
    "        src_decoded = self.src_tokenizer.decode(\n",
    "            encoder_input.numpy(),\n",
    "            split=True,\n",
    "            remove_bos=False,\n",
    "            remove_eos=False\n",
    "            )\n",
    "\n",
    "        # post processed attn scores\n",
    "        # outputs.decoder_attentions[-1]  # the last layer of self-attention scores\n",
    "\n",
    "        # draw the attention map of the last decoder block\n",
    "        for attn_score, cross_attn_score, src, trg in zip(\n",
    "            outputs.decoder_self_attn_scores[layer_idx], outputs.decoder_cross_attn_scores[layer_idx], src_decoded, trg_decoded):\n",
    "            if heads_list is None:# 如果没有指定heads_list，就画单个热力图\n",
    "                self.draw_attention_map(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                )\n",
    "            else:# 如果指定了heads_list，就画多个热力图\n",
    "                self.draw_attention_maps(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                    heads_list=heads_list,\n",
    "                    )\n",
    "        return [self.mose_detokenizer.tokenize(self.pattern.sub(\"\", s).split()) for s in self.trg_tokenizer.decode(preds)] #将解码后的目标语言句子列表返回，并使用 mose_detokenizer 进行去标记化，最终得到翻译后的结果。\n",
    "\n",
    "\n",
    "# sentence_list = [\n",
    "#     \"Mann in einem kleinen weißen Boot auf einem See.\",  # Man in a small white boat on a lake.\n",
    "#     \"Ein Mann mit einem Eimer und ein Mädchen mit einem Hut am Strand.\", # A man with a bucket and a girl in a hat on the beach.\n",
    "#     \"Drei Männer auf Pferden während eines Rennens.\",  # Three men on horses during a race.\n",
    "#     \"Ein Mann und eine Frau essen zu Abend\",  # 一个男人和一个女人在吃晚餐\n",
    "# ]\n",
    "sentence_list = [\n",
    "    \"Mann in einem kleinen weißen Boot auf einem See.\",  # Man in a small white boat on a lake.\n",
    "    # \"Ein Mann mit einem Eimer und ein Mädchen mit einem Hut am Strand.\", # A man with a bucket and a girl in a hat on the beach.\n",
    "    # \"Drei Männer auf Pferden während eines Rennens.\",  # Three men on horses during a race.\n",
    "    # \"Ein Mann und eine Frau essen zu Abend\",  # 一个男人和一个女人在吃晚餐\n",
    "]\n",
    "\n",
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "translator = Translator(model.cpu(), tokenizer, tokenizer)\n",
    "translator(\n",
    "    sentence_list,\n",
    "    layer_idx=-1,\n",
    "    # heads_list=[0, 1, 2, 3, 4, 5, 6, 7]\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "!ls checkpoints"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "e0WkUQuUe-Cy",
    "outputId": "433e3d70-4f34-4376-e4a3-1b8834e71f88"
   },
   "execution_count": null,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "translate-transformer-not-share\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# prompt: 把best.ckpt复制到云盘内\n",
    "\n",
    "!cp -r checkpoints/translate-transformer-not-share/best.ckpt /content/drive/MyDrive/transformer-de-en\n"
   ],
   "metadata": {
    "id": "_1ZLtdahywWf"
   },
   "execution_count": null,
   "outputs": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "language": "python",
   "display_name": "Python 3 (ipykernel)"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4,
  "colab": {
   "provenance": [],
   "gpuType": "T4"
  },
  "accelerator": "GPU",
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "55146ac7395840dca847e0f63aa80561": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_abb9bc76c0ce4634bd340be20c074b8c",
       "IPY_MODEL_a89d790df59245fcaf26335d4903c9b4",
       "IPY_MODEL_f837255914194dc792cf1514dee03dc5"
      ],
      "layout": "IPY_MODEL_46a51a1719d8444eb5877720253d3c37"
     }
    },
    "abb9bc76c0ce4634bd340be20c074b8c": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_013d2a6b90144432bbfb15136235faba",
      "placeholder": "​",
      "style": "IPY_MODEL_2b82e42c34804b13bc83be4c7009d4e2",
      "value": ""
     }
    },
    "a89d790df59245fcaf26335d4903c9b4": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_9233439bdd6746c28ad4a20ef47799f6",
      "max": 280,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_0e10428326bc436e8dbc079e89570bde",
      "value": 280
     }
    },
    "f837255914194dc792cf1514dee03dc5": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_0fe4ed7283c04496affe727c235d31c4",
      "placeholder": "​",
      "style": "IPY_MODEL_0c0c7229347b49ffbb9a0f7bd41199b9",
      "value": " 21020/? [21:30&lt;00:00, 15.32it/s, epoch=19, loss=2.61, val_loss=3.41]"
     }
    },
    "46a51a1719d8444eb5877720253d3c37": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "013d2a6b90144432bbfb15136235faba": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "2b82e42c34804b13bc83be4c7009d4e2": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "9233439bdd6746c28ad4a20ef47799f6": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "0e10428326bc436e8dbc079e89570bde": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "0fe4ed7283c04496affe727c235d31c4": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "0c0c7229347b49ffbb9a0f7bd41199b9": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "7190c928edb040c3a523cdd2e3f45572": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_990741ff083d457587665d8f620b69c3",
       "IPY_MODEL_00fe6a550df547cea5b1e0d84c1a3f5f",
       "IPY_MODEL_fab8b1e3187841ee894b92859fa4821b"
      ],
      "layout": "IPY_MODEL_f9b0d916e1a64c2a9f384e42f3d5dcab"
     }
    },
    "990741ff083d457587665d8f620b69c3": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_a78199650ecc47bd81f482a2671b563a",
      "placeholder": "​",
      "style": "IPY_MODEL_673bd3773a374261a01cfc48081c27f3",
      "value": "100%"
     }
    },
    "00fe6a550df547cea5b1e0d84c1a3f5f": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_83f345f7424c43929d7a304cbfb6d0fe",
      "max": 9714,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_58d69d086f644e89937347e1f9261d6f",
      "value": 9714
     }
    },
    "fab8b1e3187841ee894b92859fa4821b": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_8cf462fa996c41b18fb4571e0a757363",
      "placeholder": "​",
      "style": "IPY_MODEL_434ac4c51db74681942d2d3bd869b1df",
      "value": " 9714/9714 [00:00&lt;00:00, 236744.37it/s]"
     }
    },
    "f9b0d916e1a64c2a9f384e42f3d5dcab": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "a78199650ecc47bd81f482a2671b563a": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "673bd3773a374261a01cfc48081c27f3": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "83f345f7424c43929d7a304cbfb6d0fe": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "58d69d086f644e89937347e1f9261d6f": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "8cf462fa996c41b18fb4571e0a757363": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "434ac4c51db74681942d2d3bd869b1df": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "c9db4cd83b7b4f53a0fa93b8c365debd": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_a972aba7913b48c6973ae27bff5b9d1d",
       "IPY_MODEL_22aa774ab39e4fd78b0ff08d430d2b5d",
       "IPY_MODEL_9a01f02fb99b4b81ac487d23bc5daf4a"
      ],
      "layout": "IPY_MODEL_19f846338f6b48c9930b58575641e5b8"
     }
    },
    "a972aba7913b48c6973ae27bff5b9d1d": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_7d293204dc1b4d97bb5ee670787c7b7d",
      "placeholder": "​",
      "style": "IPY_MODEL_ea8fa440f320435aa75c9bef75bc3f08",
      "value": ""
     }
    },
    "22aa774ab39e4fd78b0ff08d430d2b5d": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_4e64bcb87e584beda0408a0aa5a659ae",
      "max": 1,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_b077d7562d814df5b6d839464dd4122f",
      "value": 1
     }
    },
    "9a01f02fb99b4b81ac487d23bc5daf4a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_5a28babf4e2e4393bdc1a093034885ab",
      "placeholder": "​",
      "style": "IPY_MODEL_be07d518c21149d196c610fd57b1eaec",
      "value": " 957/? [00:18&lt;00:00, 51.31it/s]"
     }
    },
    "19f846338f6b48c9930b58575641e5b8": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "7d293204dc1b4d97bb5ee670787c7b7d": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "ea8fa440f320435aa75c9bef75bc3f08": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "4e64bcb87e584beda0408a0aa5a659ae": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": "20px"
     }
    },
    "b077d7562d814df5b6d839464dd4122f": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "5a28babf4e2e4393bdc1a093034885ab": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "be07d518c21149d196c610fd57b1eaec": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "40f7e3754ad44b33acfe0a80b3648a3a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_b1e51beac50f4947a9dd235d8aa87603",
       "IPY_MODEL_8cd9e9702e114ebc856f56975544ede8",
       "IPY_MODEL_b7c7a718a8654956947108969557ca83"
      ],
      "layout": "IPY_MODEL_20a27d1ec76f4d85b1ffe99401fbb5b7"
     }
    },
    "b1e51beac50f4947a9dd235d8aa87603": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_e0ddf3970b3e4248bc87d4649fa9c448",
      "placeholder": "​",
      "style": "IPY_MODEL_d7bdde54e2534c49a207540bd258d3ae",
      "value": "  9%"
     }
    },
    "8cd9e9702e114ebc856f56975544ede8": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "danger",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_22484000400842f0b6e245dc610acbba",
      "max": 128,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_a66a5b4ff37d4dafa4ba3081aa1359f4",
      "value": 12
     }
    },
    "b7c7a718a8654956947108969557ca83": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_0b7866c29e5f4afe9b92de15f1b88c3e",
      "placeholder": "​",
      "style": "IPY_MODEL_333e84a304184c57af1821620ae80f6a",
      "value": " 12/128 [00:00&lt;00:01, 73.96it/s]"
     }
    },
    "20a27d1ec76f4d85b1ffe99401fbb5b7": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "e0ddf3970b3e4248bc87d4649fa9c448": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "d7bdde54e2534c49a207540bd258d3ae": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "22484000400842f0b6e245dc610acbba": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "a66a5b4ff37d4dafa4ba3081aa1359f4": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "0b7866c29e5f4afe9b92de15f1b88c3e": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "333e84a304184c57af1821620ae80f6a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
